# Lệnh và biên nhận

> Mọi thao tác ghi mang command_id của riêng bạn, nên thử lại luôn an toàn và phản hồi bị mất luôn đối chiếu được.

Mọi thao tác thay đổi dữ liệu là một **lệnh**. Bạn tạo một UUID, gửi kèm làm `command_id`, và Browlix áp dụng lệnh tối đa một lần.

## Thử lại an toàn

- Lần đầu, phản hồi là `201` với `replayed: false`.
- Gửi lại **cùng `command_id` với cùng nội dung** sẽ nhận kết quả đã lưu với `200` và `replayed: true`. Không có gì bị áp dụng hai lần.
- Gửi cùng `command_id` với **nội dung khác** là `CONFLICT`: một id chỉ mang một ý định.

Vì vậy khi yêu cầu bị quá thời gian hoặc mất kết nối, hãy gửi lại với cùng id. Đừng tạo id mới cho lần thử lại — đó sẽ là một lệnh thứ hai.

## Sau khi mất phản hồi

Nếu không chắc lệnh đã tới chưa, hãy hỏi biên nhận thay vì ghi lại:

```bash
curl https://browlix.com/api/public/v1/receipts/$COMMAND_ID \
  -H "Authorization: Bearer $BROWLIX_API_KEY"
```

`NOT_FOUND` nghĩa là lệnh chưa từng tới, nên gửi lại là an toàn. Biên nhận cho biết lệnh đã kết thúc ra sao.

## `OUTCOME_UNKNOWN`

Một số tác động xảy ra bên ngoài Browlix — trang web gửi form, webhook tới máy chủ của bạn. Khi không xác nhận được tác động của một bước, Browlix nói rõ bằng `OUTCOME_UNKNOWN` (HTTP 202) thay vì đoán, và tự động hoá không bao giờ tự lặp lại bước đó. Hãy quyết định bằng `run_targets.resolve` (`happened` / `not_happened`) hoặc kiểm tra ở hệ thống đích.

## Trong SDK, CLI và MCP

- SDK tự tạo `command_id` khi bạn không truyền và trả lại ở `reply.commandId`; lần thử lại tự động dùng lại id đó.
- CLI in ra id đã dùng; truyền `--command-id` để thử lại đúng lệnh đó.
- Tool MCP tự tạo id khi agent không truyền và trả lại trong kết quả; sau khi mất kết nối, agent gọi `receipts_get` với id đó.
