Idempotency & huỷ
Idempotency-Key, huỷ run, xác nhận hành động, trả lời câu hỏi, ngữ nghĩa resume.
Idempotency-Key
Gửi kèm mọi lời gọi gửi message (POST /v2/chat,
POST /v2/conversations/{id}/messages):
Idempotency-Key: 2f6a9c31-...Tối đa 255 ký tự, bạn tự sinh (một UUID là lựa chọn hợp lý). Retry an toàn khi mạng timeout hoặc bạn không chắc request trước có tới nơi hay không:
- Cùng key, cùng conversation, cùng end-user → trả về đúng message đã tạo ở lần gọi đầu, không tạo message thứ hai, không chạy lại run.
- Cùng key nhưng khác end-user (ví dụ một end-user khác vô tình dùng lại
key bạn đã dùng) →
422 validation_error— không bao giờ trả về message của người khác. - Với
POST /v2/chatkhông kèmconversation_id: retry cùng key sẽ quay lại đúng conversation đã tự tạo ở lần gọi đầu, thay vì tạo một conversation mới mỗi lần retry.
Không gửi Idempotency-Key vẫn hoạt động bình thường — bạn chỉ mất tính an
toàn khi retry (một retry không có key có thể tạo ra hai message).
Huỷ một run
POST https://api.harnix.vn/v2/runs/{run_id}/cancel
Authorization: Bearer <token>202 Accepted — { "status": "cancelling" }. Đây là một yêu cầu, không
phải một sự kiện tức thì: run dừng ở checkpoint an toàn tiếp theo (ví dụ
sau khi một tool call đang chạy hoàn tất), không dừng giữa chừng một cách
tuỳ tiện. Theo dõi stream của run đó để biết chính
xác lúc nào nó kết thúc — khung cuối sẽ là error với message
"cancelled".
Huỷ một run đã kết thúc rồi (thành công, thất bại, hoặc đã huỷ trước đó)
trả về run_not_cancellable (409). Một run đang chờ xác nhận hành động hoặc
đang chờ trả lời câu hỏi (xem bên dưới) huỷ được như một run đang chạy.
Xác nhận hành động
Khi agent muốn gọi một tool của connector cần xác nhận,
run dừng lại và bạn nhận khung approval trên stream
(hoặc pending_approval khi đọc lại cuộc hội thoại).
Trả lời bằng:
POST https://api.harnix.vn/v2/runs/{run_id}/approval
Authorization: Bearer <token>
Content-Type: application/json
{ "outcome": "approved", "remember": false }outcomelà"approved"(chạy tool với đúng tham số đã hiện) hoặc"denied"(không chạy; tool nhận một kết quả lỗi và trợ lý nói với người dùng rằng hành động không được thực hiện). Cả hai đều trả202với thông tin run; run chạy tiếp ngay sau đó.remember: truechỉ đi cùng"approved": những lần gọi cùng tool đó của cùng connector trong cuộc hội thoại này không hỏi lại nữa. Kết hợp khác (remembervới"denied", hoặc qua API key) trả422 validation_error.- Ai gọi được: token của end-user sở hữu cuộc hội thoại, hoặc API key của tổ
chức (
X-API-Key, cho kênh server-to-server — kênh này tự quyết, không có "ghi nhớ"). - Run không chờ xác nhận (đã quyết định rồi, đã kết thúc, hoặc chưa từng
dừng) trả
run_not_waiting(409) — kể cả lần gọi thứ hai cho cùng một run, nên bấm đúp không gây hại. Run không tồn tại hoặc không phải của bạn:run_not_found(404).
Không trả lời trong cửa sổ chờ (mặc định 5 phút, xem Giới hạn) tính như từ chối. Mọi quyết định — ai, lúc nào, kết quả — hiện trên trace của run trong console.
Trả lời câu hỏi (ask_user)
Khi agent gọi tool tích hợp ask_user để hỏi lại người dùng,
run dừng lại ở waiting_input và bạn nhận khung question trên
stream (hoặc pending_question khi đọc lại cuộc hội
thoại).
Bạn có hai cách trả lời:
Cách 1: Gọi endpoint /answer
Gửi câu trả lời có cấu trúc tương ứng với các câu hỏi:
POST https://api.harnix.vn/v2/runs/{run_id}/answer
Authorization: Bearer <token>
Content-Type: application/json
{
"answers": [
{
"selected": ["Quận 3"],
"other": null
}
]
}answers: mảng câu trả lời có độ dài bằng đúng số lượng câu hỏi trong yêu cầu (1–4 câu). Mỗi phần tử gồm:selected: danh sách nhãn lựa chọn người dùng chọn (câu hỏi chọn một chỉ được tối đa 1 nhãn; câu hỏi chọn nhiều được 0–4 nhãn hợp lệ trong các options của câu đó, không trùng lặp).other: chuỗi văn bản tự do người dùng nhập khi chọn mục khác hoặc ghi chú thêm (tối đa 2.000 ký tự sau khi cắt khoảng trắng).- Mỗi câu phải có ít nhất một lựa chọn trong
selectedhoặc một nội dungotherkhông rỗng.
- Ai gọi được: token của end-user sở hữu cuộc hội thoại, hoặc API key của tổ
chức (
X-API-Keycho kênh server-to-server). - Khi hợp lệ, trả về
202 Acceptedvới thông tin run; hệ thống tự động ghi một messagerole=uservào cuộc hội thoại với nội dung câu trả lời, và run tiếp tục chạy ngay sau đó. - Run không chờ câu hỏi (đã trả lời rồi, đã kết thúc, hoặc đang chờ xác nhận)
trả
run_not_waiting(409) — bấm đúp không gây hại. Run không tồn tại hoặc không phải của bạn:run_not_found(404). Câu trả lời không khớp với các lựa chọn của câu hỏi:validation_error(422).
Cách 2: Gửi tin nhắn tiếp theo vào cuộc hội thoại
Trong lúc một câu hỏi đang chờ, người dùng có thể gõ tin nhắn trực tiếp vào ô
chat (POST /v2/conversations/{id}/messages hoặc
POST /v2/chat). Hệ thống coi tin nhắn này là
câu trả lời văn bản tự do của người dùng:
- Trả về
202 Acceptedvới chính run đang chờ (không tạo run mới, không bị chặn bởi trần run đồng thời). - Ghi message đó vào cuộc hội thoại và tiếp tục xử lý run.
- Gửi lại với cùng
Idempotency-Keytrả về cùng run mà không tạo message thứ hai.
Hết hạn câu hỏi
Không trả lời trong cửa sổ chờ (mặc định 10 phút, cấu hình
ASK_USER_TIMEOUT_SECONDS, xem Giới hạn): câu hỏi được
hệ thống đánh dấu hết hạn. Run tiếp tục chạy, model nhận thông báo câu hỏi đã
hết hạn để hoàn thành câu trả lời với những gì đã biết, và kết thúc thành công
— không ghi thêm message người dùng nào khi hết hạn.
Ngữ nghĩa resume (khi một run được chạy lại)
Một run bị gián đoạn giữa chừng (worker restart, lỗi hạ tầng tạm thời) có thể được engine tự chạy lại từ đúng chỗ dừng, thay vì bắt đầu lại từ đầu. Điều này ảnh hưởng tới cách bạn nghĩ về tool call có tác dụng phụ (side effect):
Một tool có thể bị gọi lại đúng một lần
Một tool đã có kết quả được ghi lại (durable) không bao giờ bị gọi lại khi resume. Một tool chưa có kết quả lúc gián đoạn (nó đang chạy dở, hoặc kết quả chưa kịp ghi) có thể bị gọi lại — chấp nhận đây là chi phí đã biết, không phải một cam kết rằng tool đó tự idempotent. Nếu bạn triển khai một MCP server có tác dụng phụ (gửi email, tạo đơn hàng...), tự thêm một khoá idempotency ở phía server của bạn cho hành động đó.
Một hành động đã được người dùng xác nhận cũng chỉ chạy một lần: quyết định được ghi lại trước khi tool chạy, nên khi run tiếp tục sau một gián đoạn, Harnix không hỏi lại và không chạy lại một tool đã có kết quả.
Điều này không ảnh hưởng tới các endpoint public phía trên — nó là hành vi nội bộ của engine, nói ra ở đây vì nó quyết định độ an toàn của một tool bạn tự cấu hình qua MCP server.
Bước tiếp theo
- Streaming — xem run đổi trạng thái theo thời gian thực, kể cả khi bị huỷ.
- Tool — cấu hình MCP server.
- Lượt chạy & Trace — xem lại từng bước của một run (kể cả run đã resume); nội dung này bị xoá theo cấu hình lưu trữ trace của tổ chức bạn.