# Idempotency-Key

`Idempotency-Key` cho phép bạn gọi lại một request tạo mới mà không sợ tạo trùng, ví dụ khi mất kết nối giữa chừng và không biết lượt trước đã thành công hay chưa.

Header **tuỳ chọn**, hỗ trợ ở ba đường:

- `POST /orders` (tạo đơn);
- `POST /customers` (tạo khách);
- `POST /conversations/:id/messages` (gửi tin nhắn).

```bash
curl -X POST "https://danix.vn/api/open/v1/customers" \
  -H "Authorization: Bearer dnx_live_…" \
  -H "Idempotency-Key: a3f9d1c2-7b4e-4c6a-8d21-5e0f9b8c7a64" \
  -H "Content-Type: application/json" \
  -d @customer.json
```

Dùng một chuỗi ngẫu nhiên khó đoán, thường là UUID v4, và sinh **một lần cho mỗi ý định** (một đơn bạn muốn tạo), rồi dùng lại chính nó khi thử lại ý định ấy.

## Hành vi

| Tình huống | Kết quả |
| --- | --- |
| Lần đầu thấy khoá | Thực hiện bình thường và lưu kết quả. |
| Gửi lại cùng khoá, cùng thân, lượt trước đã xong | Phát lại **đúng phản hồi đã lưu**, kèm header `Idempotent-Replayed: true`; không tạo thêm gì. |
| Gửi lại cùng khoá khi lượt trước **còn đang chạy** | 409 [`idempotency-key-in-progress`](/developers/errors#idempotency-key-in-progress) **ngay lập tức**, kèm `Retry-After: 1`. Không chờ. Gửi lại y nguyên sau một giây. |
| Cùng khoá nhưng thân khác | 422 [`idempotency-key-reused`](/developers/errors#idempotency-key-reused). Dùng khoá mới cho nội dung mới. |

## Thời hạn và phạm vi

- Khoá được giữ **24 giờ**. Sau đó, dùng lại cùng chuỗi được coi là một khoá mới và thực hiện lượt mới.
- Khoá có phạm vi theo ứng dụng kết nối: hai ứng dụng khác nhau dùng cùng một chuỗi không đụng nhau.
- Kết quả được ghi cùng giao dịch với lượt tạo, nên không có trường hợp tạo xong mà khoá chưa ghi.
- Chỉ lượt **thành công** được giữ. Lượt bị từ chối (ví dụ 400, 422 thiếu tồn kho) huỷ cả giao dịch lẫn khoá, nên gửi lại cùng khoá sau khi sửa nguyên nhân là chạy lại thật — với cùng thân thì kết quả có thể khác lần trước.

## Với gửi tin nhắn

Gửi tin dùng cơ chế chống trùng sẵn có của chat: DANIX suy ra mã tin nhắn từ shop, ứng dụng, **hội thoại** và `Idempotency-Key`, nên thử lại cùng khoá trong cùng hội thoại không gửi tin hai lần tới khách. Những điểm khác với tạo đơn và tạo khách:

- Phạm vi là **một hội thoại**: cùng khoá gửi vào hai hội thoại khác nhau là hai tin, không phải lỗi `idempotency-key-reused`.
- Gửi lại cùng khoá trả **trạng thái hiện tại** của tin (ví dụ lần đầu `pending`, lần sau đã `sent`), không phải nguyên văn phản hồi lần đầu. Ngoại lệ: khi shop gửi tin qua hàng đợi, một tin `pending` mà hàng đợi bỏ cuộc **không** chuyển sang `failed` — nó biến khỏi `GET /conversations/{id}/messages` sau khoảng 5 phút. Đừng dựa vào trạng thái tin để biết lượt gửi qua hàng đợi đã hỏng.
- Kênh lỗi **tạm thời** (Facebook giới hạn tốc độ gọi của trang, máy chủ của kênh lỗi) trả 503 [`channel-unavailable`](/developers/errors#channel-unavailable): thử lại với **cùng** khoá và **cùng** thân là một lượt gửi mới, không phát lại lượt hỏng. Kênh từ chối **vĩnh viễn** trả 201 với `status` là `failed`, và kết quả ấy được phát lại cho cùng khoá trong 24 giờ — muốn gửi lại thì dùng khoá mới.

Sau 24 giờ, cùng một khoá ra mã tin mới và tin được gửi như một tin mới.

## Khuyến nghị

- Luôn gửi `Idempotency-Key` khi tạo đơn, tạo khách và gửi tin từ hệ thống tự động.
- Khi gặp lỗi mạng, hết giờ hoặc 5xx, thử lại với **cùng** khoá. Khi gặp 4xx khác 409, sửa request thay vì thử lại.
