# Contacts, segments và topics

Trước khi gửi chiến dịch email, bạn cần biết gửi cho ai. Hạ Tầng tổ chức người nhận giống cách Resend đang làm:

- **Contact**: mỗi địa chỉ email là một contact duy nhất trong doanh nghiệp (không phân biệt hoa thường), có họ tên, **thuộc tính tuỳ chỉnh** và cờ "huỷ đăng ký mọi email".
- **Segment**: nhóm contact cố định để gửi chiến dịch email. Một contact thuộc được nhiều segment; xoá segment không xoá contact. Trước đây segment gọi là *audience*, và API `/audiences` vẫn chạy.
- **Topic**: loại email, như khuyến mãi hay bản tin hằng tuần, mà từng người nhận tự chọn nhận hoặc bỏ trên trang tuỳ chọn.

Trong dashboard, mọi thứ nằm ở **Email → Danh sách nhận**, trong các tab **Contacts**, **Segments**, **Topics**, **Thuộc tính** và **Danh sách chặn**.

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

Các endpoint contact, segment, topic và thuộc tính cần key có `email:write`.

## Thêm contact

**cURL**

```bash
curl -X POST https://api.hatang.dev/contacts \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "email": "lan@example.vn",
    "first_name": "Lan",
    "last_name": "Trần",
    "properties": { "plan": "pro" },
    "segments": [{ "id": "aud_0199e6c17a2b7f009c1e3f6a2b1d9e10" }],
    "topics": [{ "id": "topic_0199e6c17a2c7c418f0e61f1b2a3c4d5", "subscription": "opt_in" }]
  }'
# { "object": "contact", "id": "con_…" }
```

**Node.js**

```ts
const { data, error } = await hatang.contacts.create({
  email: "lan@example.vn",
  first_name: "Lan",
  last_name: "Trần",
  properties: { plan: "pro" },
  segments: [{ id: "aud_0199e6c17a2b7f009c1e3f6a2b1d9e10" }],
  topics: [{ id: "topic_0199e6c17a2c7c418f0e61f1b2a3c4d5", subscription: "opt_in" }],
});
```

**Python**

```python
requests.post(
    "https://api.hatang.dev/contacts",
    headers={"Authorization": f"Bearer {os.environ['HATANG_API_KEY']}"},
    json={
        "email": "lan@example.vn",
        "first_name": "Lan",
        "properties": {"plan": "pro"},
        "segments": [{"id": "aud_0199e6c17a2b7f009c1e3f6a2b1d9e10"}],
    },
)
```

**PHP**

```php
$client->post('/contacts', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
    'json' => [
        'email' => 'lan@example.vn',
        'first_name' => 'Lan',
        'properties' => ['plan' => 'pro'],
        'segments' => [['id' => 'aud_0199e6c17a2b7f009c1e3f6a2b1d9e10']],
    ],
]);
```

| Trường | Mô tả |
|---|---|
| `email` | Bắt buộc. Không trùng trong doanh nghiệp. |
| `first_name`, `last_name` | Mỗi trường tối đa 100 ký tự. |
| `unsubscribed` | `true` huỷ đăng ký mọi email cho contact. |
| `properties` | Giá trị của các thuộc tính đã khai báo. `null` xoá một giá trị. |
| `segments` | Tối đa 50 `{ "id" }` để thêm contact vào. |
| `topics` | Tối đa 50 cặp `{ "id", "subscription" }`, trong đó `subscription` là `opt_in` hoặc `opt_out`. |

`POST /contacts` tạo contact, hoặc cập nhật nếu địa chỉ đã tồn tại, và trả về id. Webhook `contact.created`, `contact.updated` và `contact.deleted` bắn khi contact thay đổi. Contact mới cũng khởi động các automation dùng sự kiện `hatang:contact.created`; xem [Automations](https://hatang.dev/vi/docs/guides/email/automations.md).

Trong dashboard bạn cũng có thể **Thêm liên hệ** từng người hoặc **Nhập CSV**. Dòng đầu của CSV là tiêu đề cột, gồm các cột tuỳ chọn: `email`, `first_name`, `last_name`, `unsubscribed` và khoá các thuộc tính của bạn. Cột lạ bị bỏ qua, địa chỉ đã có thì được cập nhật. Khách hàng trong CRM cũng đồng bộ sang được (**Khách hàng → Đồng bộ sang Email**); đồng bộ không bao giờ đăng ký lại cho người đã huỷ đăng ký.

## Thuộc tính tuỳ chỉnh

Khai báo thuộc tính trước khi gán cho contact. Thuộc tính có kiểu `string` hoặc `number`, kèm giá trị dự phòng tuỳ chọn:

```bash
curl -X POST https://api.hatang.dev/contact-properties \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '{ "key": "plan", "type": "string", "fallback_value": "free" }'
```

- Khoá gồm chữ, số và `_`, tối đa 50 ký tự. Giá trị chuỗi tối đa 2.000 ký tự.
- Gán thuộc tính chưa khai báo trả về `422`. Thuộc tính `number` từ chối giá trị không phải số.
- Về sau chỉ đổi được giá trị dự phòng (`PATCH /contact-properties/{id}`). Xoá thuộc tính sẽ xoá giá trị đó khỏi mọi contact.

Dùng thuộc tính trong chiến dịch email và email của automation dạng `{{{contact.plan}}}`, cùng với `{{{contact.first_name}}}`, `{{{contact.last_name}}}` và `{{{contact.email}}}`. Contact chưa có giá trị thì lấy giá trị mặc định tại chỗ (`{{{contact.plan|free}}}`), rồi tới giá trị dự phòng của thuộc tính.

## Segments

```bash
curl -X POST https://api.hatang.dev/segments \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Khách hàng 2026" }'
# { "object": "segment", "id": "aud_…", "name": "Khách hàng 2026", "created_at": "…" }

curl -X POST https://api.hatang.dev/contacts/lan@example.vn/segments/aud_0199e6c17a2b7f009c1e3f6a2b1d9e10 \
  -H "Authorization: Bearer $HATANG_API_KEY"
```

Tên segment tối đa 100 ký tự. Id segment bắt đầu bằng `aud_`, vì segment chính là audience trước đây.

## Topics và trang tuỳ chọn

```bash
curl -X POST https://api.hatang.dev/topics \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Khuyến mãi", "description": "Giảm giá và mã ưu đãi", "default_subscription": "opt_in", "visibility": "public" }'
```

| Trường | Mô tả |
|---|---|
| `name` | Tối đa 50 ký tự. |
| `description` | Tối đa 200 ký tự, hiện trên trang tuỳ chọn. |
| `default_subscription` | `opt_in`: contact nhận cho tới khi tự bỏ. `opt_out`: contact chỉ nhận sau khi tự đăng ký. Không đổi được sau khi tạo. |
| `visibility` | `public` hiện topic cho mọi người trên trang tuỳ chọn. `private` (mặc định) chỉ hiện với contact đang nhận topic đó. |

Topic lọc thư như sau:

- Chiến dịch email có `topic_id` chỉ tới những contact đang nhận topic đó.
- `POST /emails` có `topic_id` bỏ qua người nhận đã rời topic; họ được ghi nhận là bị chặn (suppressed). Địa chỉ chưa là contact thì theo giá trị mặc định của topic.
- Lựa chọn của một contact xem ở `GET /contacts/{id}/topics` và đổi bằng `PATCH /contacts/{id}/topics`:

```bash
curl -X PATCH https://api.hatang.dev/contacts/lan@example.vn/topics \
  -H "Authorization: Bearer $HATANG_API_KEY" -H "Content-Type: application/json" \
  -d '[{ "id": "topic_0199e6c17a2c7c418f0e61f1b2a3c4d5", "subscription": "opt_out" }]'
```

Link `{{{UNSUBSCRIBE_URL}}}` trong chiến dịch email mở **trang tuỳ chọn**, nơi người nhận bật tắt từng topic hoặc huỷ đăng ký mọi email. Chỉ mở trang thì không thay đổi gì, nên các bộ quét link không vô tình huỷ đăng ký ai. Trang hiển thị tiếng Anh nếu trình duyệt ưu tiên tiếng Anh, còn lại là tiếng Việt. Đặt tiêu đề, mô tả, logo và màu ở **Cài đặt email → Trang huỷ đăng ký**. Cách huỷ đăng ký một chạm hoạt động xem ở [Chiến dịch email](https://hatang.dev/vi/docs/guides/email/broadcasts.md#unsubscribe).

## Tham chiếu API

| Endpoint | Mô tả |
|---|---|
| `POST /contacts`, `GET /contacts` | Tạo hoặc cập nhật contact; liệt kê contact (`limit`, `after`, `segment_id`, `q`). |
| `GET`, `PATCH`, `DELETE /contacts/{id hoặc email}` | Một contact. |
| `GET /contacts/{id}/segments`, `POST` hoặc `DELETE /contacts/{id}/segments/{segment_id}` | Thành viên segment. |
| `GET`, `PATCH /contacts/{id}/topics` | Lựa chọn topic. |
| `POST /segments`, `GET /segments`, `GET`, `PATCH`, `DELETE /segments/{id}`, `GET /segments/{id}/contacts` | Segment. `GET /segments/{id}` có thêm `contact_count`. |
| `POST /topics`, `GET /topics`, `GET`, `PATCH`, `DELETE /topics/{id}` | Topic. |
| `POST /contact-properties`, `GET /contact-properties`, `PATCH`, `DELETE /contact-properties/{id}` | Thuộc tính. |
| `/audiences`, `/audiences/{id}/contacts` | API audience cũ, giữ lại cho các client Resend và Hạ Tầng đang dùng. Một audience là một segment. |

SDK Node.js có các lệnh tương ứng trong `hatang.contacts`, `hatang.segments`, `hatang.topics`, `hatang.contactProperties` và `hatang.audiences`.

## Bước tiếp theo

- [Chiến dịch email](https://hatang.dev/vi/docs/guides/email/broadcasts.md)
- [Automations](https://hatang.dev/vi/docs/guides/email/automations.md)
- [Danh sách chặn và thư trả lại](https://hatang.dev/vi/docs/guides/email/suppressions-and-bounces.md)

Source: https://hatang.dev/vi/docs/guides/email/audiences
