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. |
| 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
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.
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.
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
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 }:
// 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.
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.
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.
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ư.
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. |
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.