# Xác thực API

Mọi lời gọi tới REST API tại `https://api.hatang.dev` đều xác thực bằng một **API key** gửi dưới dạng Bearer token. Trang này chỉ cách tạo key, chọn scope, giới hạn key, và API trả gì khi key thiếu, sai hoặc không đủ quyền.

## Trước khi bắt đầu

Để tạo và thu hồi key, bạn cần là chủ sở hữu, quản trị viên, hoặc thành viên có quyền **Developer** ([Nhân viên và phân quyền](https://hatang.dev/vi/help/account/employees-and-roles)).

## Tạo API key

1. Trong dashboard, mở **Cài đặt → API Keys**. Người dùng Email cũng thấy trang này ở **Email → API Keys**.
2. Bấm **Tạo key** và đặt tên cho biết key chạy ở đâu, như `production-web` hay `erp-sync`.
3. Ở mục **Key này có thể làm gì**, tích các scope cần dùng. Chỉ những module đang bật mới được liệt kê.
4. Nếu cần, giới hạn key vào một **Tên miền gửi** hoặc một số **Origin trình duyệt** (xem [Key bị giới hạn](#restricted-keys)).
5. Bấm **Tạo key** và sao chép key. Key bắt đầu bằng `ht_` và **chỉ hiện một lần**: Hạ Tầng chỉ lưu bản băm của key.

Bảng key hiện tiền tố của từng key, quyền của nó và thời điểm **Dùng lần cuối**.

## Gửi key

Đặt key vào header `Authorization` của mọi request:

```http
Authorization: Bearer ht_123...
```

**cURL**

```bash
curl "https://api.hatang.dev/emails?limit=1" \
  -H "Authorization: Bearer $HATANG_API_KEY"
```

**Node.js**

```js
import { Hatang } from "@hatang/sdk";

// SDK tự gửi header Authorization.
const hatang = new Hatang(process.env.HATANG_API_KEY, { baseUrl: "https://api.hatang.dev" });

const { data, error } = await hatang.emails.list({ limit: 1 });
```

**Python**

```python
import os
from hatang import Hatang

# SDK tự gửi header Authorization.
hatang = Hatang(os.environ["HATANG_API_KEY"], base_url="https://api.hatang.dev")

emails = hatang.emails.list(limit=1)
```

**PHP**

```php
$client = new GuzzleHttp\Client([
    'base_uri' => 'https://api.hatang.dev',
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
]);

$res = $client->get('/emails', ['query' => ['limit' => 1]]);
```

Key cũng dùng làm mật khẩu SMTP cho `smtp.hatang.dev:5870` (STARTTLS), với tên đăng nhập `hatang`.

> **Danger: Chỉ dùng ở server**
>
> API key thay mặt cho cả doanh nghiệp của bạn. Giữ key trong biến môi trường hoặc trình quản lý secret trên server. Đừng bao giờ đặt key vào JavaScript chạy trên trình duyệt, ứng dụng di động hay kho Git. Trình duyệt và ứng dụng dùng widget chat và SDK di động, vốn có site id và khoá ứng dụng công khai riêng.

## Scope {#scopes}

Scope có dạng `<sản phẩm>:<quyền>`, `<sản phẩm>:*` hoặc `*`.

| Module | Scope | Cho phép |
|---|---|---|
| Email | `email:send` | Gửi email: `POST /emails`, `POST /emails/batch` và SMTP relay |
| | `email:read` | Liệt kê và lấy email, số liệu và thư đến |
| | `email:write` | Quản lý tên miền, template, liên hệ, danh sách nhận, segment, topic, chiến dịch email và automation |
| Hỗ trợ | `chat:read`, `chat:write` | Hội thoại, tin nhắn, khách truy cập, phiếu hỗ trợ và site widget; `write` thêm quyền trả lời, giao và xử lý xong |
| Thanh toán | `payments:read`, `payments:write` | Thanh toán, giao dịch, liên kết ngân hàng và hoá đơn điện tử; `write` thêm quyền tạo thanh toán và phát hành hoá đơn |
| Sản phẩm | `products:read`, `products:write` | Sản phẩm, danh mục và đơn hàng, kể cả link thanh toán của đơn |
| CRM | `crm:read`, `crm:write` | Khách hàng, trường tuỳ chỉnh, gộp khách, xuất CSV và ZNS |
| Toàn quyền | `*` | Mọi quyền trên mọi module |

Cách các scope kết hợp:

- `write` và `send` đều bao gồm `read`. Key có `email:send` đọc được các email nó đã gửi.
- `<sản phẩm>:*` bao gồm mọi quyền của sản phẩm đó, còn `*` bao gồm mọi sản phẩm.
- Sản phẩm và CRM trước đây nằm trong Hỗ trợ: key có `chat:read`, `chat:write` hoặc `chat:*` vẫn gọi được các endpoint Sản phẩm và CRM như cũ. Key mới có thể dùng `products:*` và `crm:*` để thu hẹp quyền.
- Một vài endpoint thuộc về cả doanh nghiệp chứ không thuộc module nào. Quản lý endpoint webhook, đọc **Logs** API và đọc **Usage** cần một scope `write` bất kỳ; quản lý API key qua API cần `email:write`.

> **Info: Thư đến**
>
> Đọc thư đến (`/emails/receiving`) cần key được cấp thẳng `email:read` hoặc `email:write`. Quyền đọc đi kèm `email:send` không đủ ở đây.

Dashboard chỉ có scope của Email, Hỗ trợ và Thanh toán. Để tạo key có `products:*`, `crm:*` hoặc `*`, gọi `POST /api-keys` bằng một key có `email:write`:

```bash
curl -X POST https://api.hatang.dev/api-keys \
  -H "Authorization: Bearer $HATANG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "erp-sync", "scopes": ["products:*", "crm:read"] }'
```

```json
{ "id": "key_123...", "token": "ht_123..." }
```

Key cần đúng scope **và** module tương ứng phải đang bật. Gọi endpoint của module đang tắt nhận `403 product_disabled` dù key đủ scope ([Doanh nghiệp và module](https://hatang.dev/vi/help/getting-started/modules)).

## Key bị giới hạn {#restricted-keys}

Hai giới hạn tuỳ chọn giúp thu hẹp thiệt hại nếu key bị lộ.

| Giới hạn | Đặt ở đâu | Tác dụng |
|---|---|---|
| **Tên miền gửi** | Dashboard, hoặc `domain_id` khi gọi `POST /api-keys` | Key chỉ gửi được từ tên miền đã xác minh đó. Gửi từ tên miền khác nhận `401 restricted_api_key`. Chỉ áp dụng cho key có scope Email. |
| **Origin trình duyệt** | Dashboard: **Key này chạy ở đâu?** → **Trong trình duyệt**, rồi mỗi dòng một origin, như `https://shop.vn` | Request phải mang header `Origin` nằm trong danh sách. Mọi request khác, **kể cả request từ server không có header `Origin`**, nhận `401 restricted_api_key`. |

Chọn **Trên server của bạn** (mặc định) cho key dùng từ backend; key đó không bị khoá theo origin. Chỉ chọn **Trong trình duyệt** cho key được nạp trong trình duyệt. Thông báo lỗi sẽ nói rõ khi một key chỉ dành cho trình duyệt được gọi mà không có header `Origin`.

## Key test và môi trường Thanh toán

Chỉ module Thanh toán có môi trường. Mỗi doanh nghiệp có môi trường **development** (ngân hàng giả lập và sandbox của nhà cung cấp hoá đơn điện tử) và **production** (tiền thật). Dữ liệu thanh toán không bao giờ lẫn giữa hai môi trường. Email, Hỗ trợ, Sản phẩm và CRM không có môi trường.

Chọn môi trường cho từng request bằng header `Hatang-Environment`:

```bash
curl https://api.hatang.dev/payments \
  -H "Authorization: Bearer $HATANG_API_KEY" \
  -H "Hatang-Environment: development"
```

| Key | Không có header | `Hatang-Environment: development` | `Hatang-Environment: production` |
|---|---|---|---|
| `ht_...` | production | development | production |
| `ht_test_...` | development | development | `403 environment_mismatch` |

- Mọi response lặp lại môi trường đã dùng trong header `Hatang-Environment`.
- Giá trị header khác hai giá trị trên nhận `422 validation_error`.
- Với SDK Node.js, truyền `environment: "development"` vào constructor `Hatang`, hoặc đặt biến `HATANG_ENVIRONMENT`.
- Key bắt đầu bằng `ht_test_` là key development từ các phiên bản Hạ Tầng trước. Chúng không bao giờ chạm được dữ liệu production. Key mới bắt đầu bằng `ht_` và dùng header.

Xem [Môi trường Thanh toán](https://hatang.dev/vi/docs/guides/payments/environments.md) để biết hai môi trường khác nhau ở đâu.

## Xoay và thu hồi key

Xoay key khi người từng biết key nghỉ việc, khi key có thể đã lộ, hoặc theo lịch định kỳ:

1. Tạo key mới với cùng scope và giới hạn.
2. Triển khai key mới ở mọi nơi đang dùng key cũ.
3. Theo dõi cột **Dùng lần cuối** của key cũ cho tới khi nó ngừng thay đổi.
4. Bấm **Thu hồi** ở key cũ, hoặc gọi `DELETE /api-keys/{id}`.

Key bị thu hồi ngừng hoạt động trong vòng một phút. Không thể khôi phục key đã thu hồi; hãy tạo key mới.

## Lỗi

| Status | `name` | Khi nào |
|---|---|---|
| 401 | `missing_api_key` | Thiếu header `Authorization: Bearer` |
| 403 | `invalid_api_key` | Key sai định dạng, không tồn tại hoặc đã bị thu hồi |
| 401 | `restricted_api_key` | Key thiếu scope, được gọi từ origin không cho phép, hoặc gửi từ tên miền mà key không được khoá vào |
| 403 | `product_disabled` | Module của endpoint đang tắt với doanh nghiệp |
| 403 | `environment_mismatch` | Key `ht_test_` đòi production, hoặc thao tác Thanh toán không có trong môi trường này |
| 422 | `validation_error` | `Hatang-Environment` không phải `development` hay `production` |

```json
{
  "statusCode": 401,
  "name": "restricted_api_key",
  "message": "This API key does not have the payments:write scope"
}
```

Rẽ nhánh theo `name`, không theo `message`: message viết cho người đọc và có thể đổi. Không lỗi nào ở trên tự hết khi thử lại; hãy sửa key, scope hoặc bật module trước. Xem [Lỗi](https://hatang.dev/vi/docs/guides/getting-started/errors.md) để có bức tranh đầy đủ.

## Bước tiếp theo

- [Lỗi](https://hatang.dev/vi/docs/guides/getting-started/errors.md)
- [Giới hạn tần suất](https://hatang.dev/vi/docs/guides/getting-started/rate-limits.md)
- [Bảo mật](https://hatang.dev/vi/docs/guides/getting-started/security.md)
- [Môi trường Thanh toán](https://hatang.dev/vi/docs/guides/payments/environments.md)

Source: https://hatang.dev/vi/docs/guides/getting-started/authentication
