> ## Documentation Index
> Fetch the complete documentation index at: https://docs.econtractid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Mã lỗi

> Danh sách mã lỗi Integration API và cách xử lý

## Cấu trúc response lỗi

```json theme={null}
{
  "statusCode": 400,
  "code": "INVALID_FILE_URL",
  "message": "fileUrl must use HTTPS",
  "data": null,
  "timestamp": "2026-05-15T10:00:00Z"
}
```

| Trường       | Mô tả                                                                                                        |
| ------------ | ------------------------------------------------------------------------------------------------------------ |
| `statusCode` | HTTP status code                                                                                             |
| `code`       | Mã lỗi ổn định — dùng để xử lý logic (xem lưu ý về lỗi `401` bên dưới)                                       |
| `message`    | Mô tả cho người đọc, có thể thay đổi giữa các phiên bản                                                      |
| `data`       | Luôn `null` với response lỗi                                                                                 |
| `details`    | **Chỉ xuất hiện khi là mảng.** Một số lỗi nghiệp vụ trả thêm mảng chi tiết; phần lớn lỗi không có trường này |

<Warning>
  **Với lỗi `401`, `code` luôn là `UNAUTHORIZED`** — không phải `API_KEY_INVALID` hay các mã cụ thể khác. Nguyên nhân chi tiết chỉ nằm trong `message`.

  Nghĩa là: với `4xx` khác thì rẽ nhánh theo `code`; riêng `401` phải đọc `message` (hoặc đơn giản là coi mọi `401` là "thông tin xác thực có vấn đề" và kiểm tra lại API Key).
</Warning>

***

## 400 — Dữ liệu đầu vào

| Code                      | Nguyên nhân                                                                                        | Cách xử lý                                                       |
| ------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `TENANT_DISABLED`         | Không xác định được tổ chức (API Key sai hoặc tài khoản dịch vụ bị vô hiệu hoá)                    | Kiểm tra API Key, liên hệ quản trị viên                          |
| `INVALID_PROCESS_DEF`     | `processDefCode` không tồn tại trong tổ chức                                                       | Đối chiếu mã quy trình đã được cấu hình                          |
| `INVALID_TEMPLATE_CODE`   | `files[].templateCode` không tồn tại khi `source=template`                                         | Kiểm tra mã mẫu tài liệu                                         |
| `INVALID_FILE_URL`        | `fileUrl` sai định dạng, không phải HTTPS, thiếu host, trỏ vào dải IP nội bộ, hoặc URL có redirect | Dùng URL HTTPS công khai, trỏ thẳng tới file, không qua redirect |
| `INVALID_SIGNER`          | `signers[].stepCode` thiếu hoặc không khớp bước nào trong quy trình                                | Đối chiếu `stepCode` với cấu hình quy trình                      |
| `INVALID_SIGNER_USERNAME` | `signers[].username` không tồn tại hoặc không thuộc tổ chức của bạn                                | Kiểm tra username, hoặc dùng thông tin khách mời thay thế        |
| `MISSING_ECM_CLASS_CODE`  | Quy trình ký từ 2 loại tài liệu trở lên nhưng file không khai `ecmClassCode`                       | Gửi `ecmClassCode` cho từng file                                 |
| `INVALID_REQUEST`         | `fileUuid` thiếu, sai định dạng, hoặc trỏ vào hồ sơ thay vì file                                   | Kiểm tra lại `fileUuid`                                          |

***

## 401 — Xác thực

Mọi lỗi `401` đều trả `code: "UNAUTHORIZED"`. Phân biệt nguyên nhân qua `message`:

| `message` chứa                    | Nguyên nhân                                                               | Cách xử lý                                                                                                 |
| --------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| API key không hợp lệ              | Key sai, không tồn tại, sai định dạng, **đã bị thu hồi, hoặc đã hết hạn** | Kiểm tra key; nếu key từng chạy được thì nhiều khả năng đã bị thu hồi hoặc hết hạn — liên hệ quản trị viên |
| API key chưa gắn người dùng       | Key chưa được bind userId                                                 | Quản trị viên gán lại qua Admin UI → API Keys → Edit                                                       |
| Tài khoản dịch vụ không hoạt động | User gắn với key đã bị vô hiệu hoá                                        | Liên hệ quản trị viên kích hoạt lại                                                                        |

<Note>
  Key bị **thu hồi** và key **hết hạn** đều trả về cùng thông báo "API key không hợp lệ" — hệ thống không phân biệt hai trường hợp này ra ngoài. Đây là chủ đích để không lộ trạng thái key cho bên gọi không hợp lệ.
</Note>

<Warning>
  **Không gửi header `X-API-Key` thì không nhận được `401` của API Key.** Request rơi xuống tầng xác thực JWT và trả về `401` chung chung. Nếu gặp `401` khó hiểu, việc đầu tiên cần kiểm tra là header đã được gửi đi hay chưa (proxy hoặc API gateway có thể đã lược bỏ).
</Warning>

Xem thêm: [Xác thực](/api-reference/xac-thuc).

***

## 413 / 415 / 502 — Lỗi khi tải file từ `fileUrl`

Khi tạo hồ sơ với `files[].source = "url"`, server tự tải file về. Các lỗi ở bước này có status riêng, **không phải `400`**:

| HTTP  | Code                    | Nguyên nhân                                                                               | Cách xử lý                                                     |
| ----- | ----------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `413` | `FILE_TOO_LARGE`        | File vượt quá 50MB                                                                        | Nén hoặc tách file                                             |
| `415` | `UNSUPPORTED_FILE_TYPE` | Nội dung tải về không phải PDF                                                            | Kiểm tra file thật sự là PDF                                   |
| `502` | `FILE_FETCH_FAILED`     | Không phân giải được tên miền, máy chủ nguồn trả về mã khác `2xx`, hoặc quá thời gian chờ | Kiểm tra URL truy cập được công khai và phản hồi trong 30 giây |

<Note>
  Hệ thống nhận diện PDF bằng **chữ ký nhị phân đầu file**, không dựa vào header `Content-Type`. File có `Content-Type` đúng nhưng nội dung không phải PDF vẫn bị từ chối với `415`; ngược lại, file PDF hợp lệ phục vụ dưới `application/octet-stream` vẫn được chấp nhận.
</Note>

***

## 404 — Không tìm thấy

| Code                 | Nguyên nhân                                          | Cách xử lý          |
| -------------------- | ---------------------------------------------------- | ------------------- |
| `DOCUMENT_NOT_FOUND` | `documentId` không tồn tại trong tổ chức             | Kiểm tra mã hồ sơ   |
| `FILE_NOT_FOUND`     | `fileUuid` không tồn tại **hoặc** thuộc tổ chức khác | Kiểm tra `fileUuid` |

<Note>
  `FILE_NOT_FOUND` gộp hai trường hợp "không tồn tại" và "thuộc tổ chức khác" một cách có chủ đích, để không lộ thông tin về dữ liệu của tổ chức khác.
</Note>

***

## 409 — Xung đột

| Code                    | Nguyên nhân                             | Cách xử lý                                                                                                                                                               |
| ----------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `DUPLICATE_DOCUMENT_ID` | `documentId` đã được dùng trong tổ chức | Đây là cơ chế idempotency. Nếu bạn đang thử lại một request có thể đã thành công, gọi [`GET /documents/{documentId}`](/api-reference/endpoint/ho-so) để lấy hồ sơ đã tạo |
