# Chuyển từ Resend

Hạ Tầng Email theo API của Resend: cùng endpoint, cùng cấu trúc request và response, cùng dạng lỗi và cùng cách ký webhook. Phần lớn code chuyển sang được chỉ bằng cách đổi base URL và API key. Trang này liệt kê những gì tương thích, những gì khác, và các bước chuyển mà không mất thư.

## Những gì tương thích

| Hạng mục | Tương thích |
|---|---|
| Gửi email | `POST /emails` với `from`, `to`, `cc`, `bcc`, `reply_to`, `subject`, `html`, `text`, `headers`, `tags`, `attachments` (dùng `content`), `scheduled_at`, `template`; `POST /emails/batch`; `GET /emails/{id}`; `PATCH /emails/{id}` để dời giờ; `POST /emails/{id}/cancel`. |
| Idempotency | Header `Idempotency-Key` trên `POST /emails`. |
| Lỗi | Body `{ "statusCode", "name", "message" }` và các tên như `validation_error`, `restricted_api_key`, `rate_limit_exceeded`, `invalid_idempotent_request`. |
| Tên miền | `POST /domains` với `name` và `custom_return_path`, `GET`, `PATCH` (`open_tracking`, `click_tracking`, `tls`, `capabilities.receiving`), `DELETE`, `POST /domains/{id}/verify`. |
| API key | `POST /api-keys` với `permission` (`full_access` hoặc `sending_access`) và `domain_id`. |
| Webhook | Quản lý endpoint, tên sự kiện (`email.sent`, `email.delivered`, `email.bounced`, `email.complained`, `email.opened`, `email.clicked`, `email.received`, `contact.*`, `domain.*`…) và cấu trúc payload. Request được ký theo kiểu Svix, với header `svix-id`, `svix-timestamp` và `svix-signature` (cùng giá trị ở `webhook-id`, `webhook-timestamp`, `webhook-signature`), nên code xác minh hiện có vẫn chạy với secret `whsec_…` mới. |
| Contact | `/contacts`, `/segments`, `/topics`, `/contact-properties` và API `/audiences` cũ. |
| Chiến dịch email | `POST /broadcasts` với `segment_id` (hoặc `audience_id`), `topic_id`, `/send` với `scheduled_at`, `/cancel`. |
| Templates | `/templates`, `/publish`, `/duplicate`, cú pháp `{{{VAR}}}` có giá trị mặc định tại chỗ, và `{{{RESEND_UNSUBSCRIBE_URL}}}`. |
| Automations | `/events`, `/events/send`, `/automations` và các lượt chạy. |
| Nhận email | `/emails/receiving` và tệp đính kèm. |
| Số liệu | `GET /emails/metrics`. |

## Những gì khác

| Chủ đề | Resend | Hạ Tầng |
|---|---|---|
| Base URL | `https://api.resend.com` | `https://api.hatang.dev` |
| API key | `re_…` | `ht_…`. Key Resend không dùng được. Key có phạm vi (`email:send`, `email:write`…); `permission` vẫn được nhận và chuyển thành phạm vi. |
| Tệp đính kèm theo URL | `path` | Chưa hỗ trợ: lỗi `422 invalid_attachment`. Gửi `content` dạng base64. |
| Gửi theo lô | | `/emails/batch` không hỗ trợ `Idempotency-Key`. Lô không phải "tất cả hoặc không gì cả": các phần tử trước phần tử lỗi đã được gửi. |
| `react` | SDK render | Giống: SDK Node.js render bằng `@react-email/render`; API chỉ nhận `html` và `text`. |
| `region` của tên miền | Chọn vùng gửi | Được nhận và lưu lại; việc gửi luôn dùng vùng Amazon SES của nền tảng. |
| Bản ghi DKIM | `resend._domainkey` | `rs<ngẫu nhiên>._domainkey`, khoá mới cho mỗi tên miền. |
| Giới hạn tốc độ | Theo gói của Resend | 10 request mỗi giây cho mỗi doanh nghiệp. |
| SMTP | `smtp.resend.com`, nhiều cổng, user `resend` | `smtp.hatang.dev:5870`, chỉ STARTTLS, user `hatang`. Xem [SMTP relay](https://hatang.dev/vi/docs/guides/email/smtp.md). |
| Danh sách chặn | | Chỉ trong dashboard (**Email → Danh sách nhận → Danh sách chặn**), không có API. |
| Thêm | | `topic_id` trên `POST /emails`, `GET /emails/{id}/events` có kết quả SMTP của từng người nhận, tìm kiếm và bộ lọc trên `GET /emails`, `GET /logs` và `GET /usage`. |

## Chuyển từng bước

### 1. Thêm tên miền vào Hạ Tầng {#1-thêm-tên-miền-vào-hatang}

Thêm từng tên miền gửi ở **Email → Tên miền** và thêm các bản ghi mới bên cạnh bản ghi của Resend. Bản ghi DKIM dùng selector khác nên không xung đột.

> **Caution: Chọn return path khác khi cả hai cùng chạy**
>
> Resend và Hạ Tầng đều dùng `send.` làm tên miền phụ return path mặc định, mỗi bên có bản ghi MX nhận phản hồi Amazon SES riêng. Để không có hai bộ bản ghi thư trả lại trên cùng một tên, hãy chọn **Return-Path tuỳ chỉnh** khác trong Hạ Tầng (ví dụ `hsend`) khi thêm tên miền, hoặc gỡ bản ghi của Resend trước nếu bạn ngừng Resend cùng lúc.

Xác minh tên miền và chờ trạng thái **Đã xác minh**. Xem [Tên miền](https://hatang.dev/vi/docs/guides/email/domains.md).

### 2. Tạo API key

Tạo một key cho mỗi hệ thống gửi email, chỉ với những phạm vi nó cần (`email:send` để gửi). Giới hạn key vào một tên miền nếu hệ thống đó chỉ nên gửi từ tên miền ấy.

### 3. Trỏ code sang Hạ Tầng {#3-trỏ-code-sang-hatang}

Cách đơn giản nhất là chuyển sang `@hatang/sdk`, có cùng tên phương thức và cùng kết quả `{ data, error }`:

```ts
// Trước
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({ from, to, subject, html });

// Sau
import { Hatang } from "@hatang/sdk";
const hatang = new Hatang(process.env.HATANG_API_KEY, { baseUrl: "https://api.hatang.dev" });
await hatang.emails.send({ from, to, subject, html });
```

Các lệnh tương ứng có trong `emails`, `batch`, `domains`, `apiKeys`, `webhooks`, `audiences`, `contacts`, `segments`, `topics`, `contactProperties`, `broadcasts`, `templates`, `events` và `automations`, và `emails.send(payload, { idempotencyKey })` nhận cùng tuỳ chọn. Xem [SDK Node.js](https://hatang.dev/vi/docs/guides/sdks/node.md).

Nếu gọi HTTP API trực tiếp, đổi base URL thành `https://api.hatang.dev` và key thành key `ht_…` của bạn. Không cần đổi gì khác.

### 4. Tạo lại webhook

Tạo các endpoint webhook trong Hạ Tầng (mục **Webhooks**, hoặc `POST /webhooks`) với cùng các sự kiện, rồi đặt signing secret mới vào code xác minh. Xem [Webhook](https://hatang.dev/vi/docs/guides/getting-started/webhooks.md).

### 5. Chuyển contact

Xuất contact từ Resend và nhập CSV ở **Email → Danh sách nhận** (**Nhập CSV**), hoặc tạo bằng `POST /contacts`. Nhập những người đã huỷ đăng ký với `unsubscribed` là `true` để họ vẫn ở trạng thái huỷ đăng ký. Tạo lại topic và segment trước để gán contact vào.

### 6. Đổi thông tin SMTP

Ứng dụng đang gửi qua SMTP của Resend cần host, cổng, tên đăng nhập và mật khẩu mới. Xem [SMTP relay](https://hatang.dev/vi/docs/guides/email/smtp.md).

### 7. Theo dõi những ngày đầu

Theo dõi **Email → Số liệu** và **Email → Logs** để thấy thư bị trả lại, báo spam và lệnh gọi lỗi. Khoá DKIM mới và return path mới chưa có lịch sử; hãy tăng dần các đợt gửi lớn như trong [Khả năng vào hộp thư](https://hatang.dev/vi/docs/guides/email/deliverability.md).

## Chuyển từ SendGrid hoặc Postmark

Hạ Tầng không có lớp tương thích cho các API này, nhưng request gửi thư của chúng ánh xạ từng trường sang `POST /emails`.

### SendGrid (v3 Mail Send)

| SendGrid | Hạ Tầng `POST /emails` |
|---|---|
| `from.email`, `from.name` | `from: "Tên <email>"` |
| `personalizations[].to`, `.cc`, `.bcc` | `to`, `cc`, `bcc`. Một personalization là một email; nhiều personalization thì dùng `POST /emails/batch`. |
| `personalizations[].dynamic_template_data` | `template.variables` |
| `template_id` | `template.id` (id hoặc alias của template trên Hạ Tầng) |
| `subject` | `subject` |
| `content[]` với `text/plain` và `text/html` | `text` và `html` |
| `reply_to` | `reply_to` |
| `headers` | `headers` |
| `categories`, `custom_args` | `tags` (cặp `name` và `value`) |
| `attachments[].content`, `.filename`, `.type`, `.content_id` | `attachments[].content`, `.filename`, `.content_type`, `.content_id` |
| `send_at` (Unix timestamp) | `scheduled_at` (ISO 8601) |

### Postmark (`/email`)

| Postmark | Hạ Tầng `POST /emails` |
|---|---|
| `From` | `from` |
| `To`, `Cc`, `Bcc` (cách nhau bởi dấu phẩy) | `to`, `cc`, `bcc` (mảng) |
| `Subject` | `subject` |
| `HtmlBody`, `TextBody` | `html`, `text` |
| `ReplyTo` | `reply_to` |
| `Headers` (`[{ "Name", "Value" }]`) | `headers` (`{ "Name": "Value" }`) |
| `Tag`, `Metadata` | `tags` |
| `Attachments[].Name`, `.Content`, `.ContentType`, `.ContentID` | `attachments[].filename`, `.content`, `.content_type`, `.content_id` |
| `TemplateAlias`, `TemplateModel` (`/email/withTemplate`) | `template.id`, `template.variables` |
| `TrackOpens`, `TrackLinks` | Theo tên miền: `open_tracking`, `click_tracking`. Xem [Theo dõi mở và nhấp](https://hatang.dev/vi/docs/guides/email/tracking.md). |

Sự kiện webhook của hai dịch vụ này tương ứng với `email.delivered`, `email.bounced`, `email.complained`, `email.opened` và `email.clicked` của Hạ Tầng. Webhook của Hạ Tầng được ký bằng header kiểu Svix; xem [Webhook](https://hatang.dev/vi/docs/guides/getting-started/webhooks.md).

## Bước tiếp theo

- [Tên miền](https://hatang.dev/vi/docs/guides/email/domains.md)
- [Gửi email](https://hatang.dev/vi/docs/guides/email/sending.md)
- [SDK Node.js](https://hatang.dev/vi/docs/guides/sdks/node.md)

Source: https://hatang.dev/vi/docs/guides/email/migrate-from-resend
