Nâng cao & tích hợp

Tích hợp email với website và CRM qua API & webhook

Khi danh sách còn nhỏ, bạn có thể nhập liên hệ bằng file CSV và gửi chiến dịch thủ công. Nhưng đến lúc website có hàng trăm lượt đăng ký mỗi ngày, CRM ghi nhận đơn hàng liên tục, và đội sale cập nhật trạng thái khách theo giờ — thao tác tay không còn theo kịp. Lúc này bạn cần tích hợp: để website, CRM và Mailemdi tự “nói chuyện” với nhau qua API (bạn chủ động đẩy dữ liệu sang Mailemdi) và webhook (Mailemdi chủ động báo sự kiện ngược về cho bạn). Bài này dành cho người đã nắm cơ bản email marketing và muốn dựng một luồng dữ liệu tự động, đáng tin cậy giữa các hệ thống.

API vs. Webhook — phân biệt nhanh: API là khi bạn gọi sang Mailemdi để hỏi hoặc thay đổi dữ liệu (“tạo liên hệ này”, “lấy số liệu chiến dịch”). Webhook là chiều ngược lại: Mailemdi gọi sang bạn mỗi khi có sự kiện (ai đó mở mail, click, bounce, hủy đăng ký). Một tích hợp trưởng thành luôn dùng cả hai chiều.

Vì sao nên tích hợp thay vì làm tay

Tích hợp không chỉ để “cho oai” — nó giải quyết những vấn đề rất cụ thể mà thao tác thủ công không thể:

  • Dữ liệu luôn tươi mới. Người dùng đăng ký trên website lúc 2 giờ sáng sẽ vào danh sách ngay, không phải chờ bạn export CSV cuối tuần. Email chào mừng có thể bắn ra trong vài giây.
  • Một nguồn sự thật. Khi CRM là “nguồn sự thật” về khách hàng, mọi thay đổi (đổi email, hủy đăng ký, lên hạng VIP) được đẩy sang Mailemdi tự động — tránh cảnh hai hệ thống lệch nhau.
  • Phản hồi theo hành vi. Webhook báo “khách vừa click link sản phẩm X” cho phép CRM chấm điểm lead, giao việc cho sale, hoặc kích hoạt một luồng chăm sóc — ngay lúc khách đang quan tâm.
  • Quy mô không giới hạn. Tạo hàng loạt tới 1000 liên hệ mỗi lần gọi, gửi email giao dịch (transactional) theo từng sự kiện đơn hàng — những việc bất khả thi nếu làm tay.

Ví dụ một vòng tròn khép kín: khách đăng ký nhận tin trên website → website gọi POST /contacts tạo liên hệ trong Mailemdi → Mailemdi gửi email chào mừng → khách mở & click → Mailemdi bắn webhook email.clicked về CRM → CRM cộng điểm quan tâm và tạo task cho sale. Toàn bộ chạy tự động, không ai phải copy dữ liệu giữa các tab.

1. Lấy và bảo mật API key (header X-API-Key)

Mọi lời gọi tới Public API v1 của Mailemdi đều xác thực bằng một header duy nhất: X-API-Key. Bạn tạo khóa trong Cài đặt → API keys của bảng điều khiển. Khóa được hiển thị đầy đủ đúng một lần khi vừa tạo — hãy chép ngay và cất nơi an toàn, vì sau đó hệ thống chỉ cho thấy bản đã che.

Lời gọi đơn giản nhất — lấy số liệu tổng quan workspace:

curl https://app.mailemdi.io.vn/api/v1/stats \
  -H "X-API-Key: $MAILEMDI_API_KEY"

Vài điều cốt lõi cần nhớ về tầng xác thực & truyền tải:

  • Base URL: mọi endpoint nằm dưới /api/v1. Phản hồi luôn được bọc trong { "data": ... }; endpoint trả danh sách kèm thêm pagination (page, limit, total, pages).
  • Phạm vi (scope): dữ liệu tự động giới hạn trong workspace của khóa — không thể vô tình đọc/ghi sang workspace khác.
  • Giới hạn tần suất: tối đa 120 request/phút mỗi khóa. Vượt mức sẽ bị từ chối, nên hãy cài cơ chế chờ-rồi-thử-lại (backoff) ở phía bạn.
  • Lỗi xác thực: thiếu/sai khóa trả về 401 kèm thân { "error": "...", "code": "INVALID_API_KEY" }.

Tuyệt đối không để lộ khóa ở trình duyệt. X-API-Key là khóa bí mật phía máy chủ. Đừng bao giờ nhúng nó vào JavaScript phía client/HTML của website — ai mở DevTools cũng đọc được và có thể thao túng toàn bộ danh sách của bạn. Hãy để website gọi đến backend của chính bạn, rồi backend mới kèm khóa gọi sang Mailemdi. Lưu khóa trong biến môi trường (MAILEMDI_API_KEY), không commit vào git. Nếu nghi lộ, tạo khóa mới và thu hồi khóa cũ ngay.

2. Thêm và đồng bộ liên hệ qua API

Đây là tích hợp phổ biến nhất: mỗi khi có người đăng ký trên website hoặc một bản ghi mới trong CRM, bạn đẩy họ vào Mailemdi. Chỉ email là bắt buộc; các trường khác như firstName, lastName, company, phone, sourcecustomFields đều tùy chọn.

curl -X POST https://app.mailemdi.io.vn/api/v1/contacts \
  -H "X-API-Key: $MAILEMDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "khachhang@example.com",
    "firstName": "An",
    "company": "Cong ty ABC",
    "source": "website-form",
    "customFields": { "goi": "Pro", "ma_kh": "KH-00123" }
  }'

# Phan hoi 201
{ "data": { "id": "ct_...", "email": "khachhang@example.com", "status": "SUBSCRIBED" } }

Hai trường nhỏ nhưng cực kỳ hữu ích khi tích hợp:

  • source — ghi rõ liên hệ đến từ đâu (website-form, crm-import, landing-blackfriday…). Sau này phân khúc theo nguồn để đo kênh nào hiệu quả.
  • customFields — túi chứa dữ liệu riêng của bạn (mã khách hàng trong CRM, gói dịch vụ, ngày hết hạn…). Đây chính là cầu nối để đối chiếu một liên hệ Mailemdi với đúng bản ghi trong CRM.

Xử lý trùng (idempotency) — đừng để tạo nhân đôi

Khi gọi POST /contacts với một email đã tồn tại, API trả về 409 kèm mã DUPLICATE. Đây không phải lỗi của bạn — đó là cơ chế bảo vệ. Tích hợp tốt phải xử lý nhánh trùng một cách chủ động: nếu nhận 409, hãy chuyển sang gọi PATCH /contacts/{id} để cập nhật thông tin mới nhất thay vì bỏ qua. Mẫu logic “tạo-nếu-chưa-có, cập-nhật-nếu-đã-có” (thường gọi là upsert) giúp đồng bộ CRM → Mailemdi luôn nhất quán dù bạn chạy lại nhiều lần.

Đồng bộ hàng loạt khi nhập kho khách hàng lớn

Khi cần đẩy cả kho khách từ CRM (lần đầu thiết lập, hoặc đồng bộ định kỳ ban đêm), đừng gọi POST /contacts hàng nghìn lần — vừa chậm vừa dễ chạm trần 120 request/phút. Dùng endpoint hàng loạt POST /contacts/bulk, cho phép tới 1000 liên hệ mỗi lời gọi. Chia kho thành các lô (batch) 1000, gọi tuần tự, và ghi log lô nào đã xong để có thể chạy tiếp nếu gián đoạn.

Việc cần làmEndpoint nên dùng
Thêm 1 liên hệ khi có đăng ký mớiPOST /contacts
Nhập kho khách hàng từ CRM (lô lớn)POST /contacts/bulk (≤1000/lần)
Cập nhật thông tin khi liên hệ đã tồn tạiPATCH /contacts/{id}
Tìm/kiểm tra liên hệ theo emailGET /contacts?q=...
Đưa liên hệ vào một danh sách cụ thểPOST /lists/{id}/contacts

Với danh sách thu thập từ website, hãy bật double opt-in để chắc chắn người đăng ký là thật, và đọc thêm nguyên tắc xây danh sách từ con số 0 — API chỉ là đường ống; chất lượng dữ liệu chảy qua mới quyết định kết quả.

3. Nhận sự kiện realtime bằng webhook

API để bạn đẩy dữ liệu sang Mailemdi. Webhook để Mailemdi đẩy ngược sự kiện về cho bạn, ngay khi chúng xảy ra — thay vì bạn phải liên tục hỏi “có gì mới chưa?” (polling). Bạn đăng ký một URL công khai và chọn các loại sự kiện muốn nhận:

curl -X POST https://app.mailemdi.io.vn/api/v1/webhooks \
  -H "X-API-Key: $MAILEMDI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://he-thong-cua-ban.com/webhook/mailemdi",
    "events": ["email.opened", "email.clicked", "email.bounced"]
  }'

# Phan hoi 201 — luu lai "secret"!
{ "data": { "id": "wh_...", "url": "...", "events": ["email.opened","email.clicked","email.bounced"],
            "isActive": true, "secret": "whsec_..." } }

Khi tạo, hệ thống sinh một secret dạng whsec_… chỉ trả về một lần duy nhất. Hãy lưu lại — bạn sẽ dùng nó để xác minh chữ ký mỗi lần nhận sự kiện. Các lần gọi GET /webhooks về sau chỉ thấy bản đã che.

Các sự kiện được hỗ trợ

Sự kiệnÝ nghĩa
contact.subscribedLiên hệ mới đăng ký / được tạo
contact.unsubscribedLiên hệ hủy đăng ký
email.sentEmail đã được gửi đi
email.openedNgười nhận mở email
email.clickedNgười nhận bấm liên kết
email.bouncedEmail bị trả về (bounce)

Xác minh chữ ký HMAC — bước bảo mật bắt buộc

URL webhook của bạn là công khai, nên về lý thuyết bất kỳ ai biết địa chỉ cũng có thể gửi dữ liệu giả tới đó. Để chắc một request thật sự đến từ Mailemdi, mỗi lần gửi hệ thống gắn hai header: X-Webhook-Event (tên sự kiện) và X-Webhook-Signature dạng sha256=<hex>. Chữ ký này là HMAC-SHA256 của nguyên văn thân JSON, dùng secret của bạn làm khóa. Phía bạn tính lại đúng công thức đó và so khớp — chỉ tin payload khi hai chuỗi trùng nhau.

POST /webhook/mailemdi  HTTP/1.1
Content-Type: application/json
X-Webhook-Event: email.clicked
X-Webhook-Signature: sha256=9f86d081884c7d65...

{
  "event": "email.clicked",
  "timestamp": "2026-06-26T03:00:00.000Z",
  "data": { "contactId": "ct_...", "email": "khachhang@example.com", "url": "https://..." }
}

# Node.js (Express) — xac minh truoc khi tin payload
import { createHmac, timingSafeEqual } from "crypto";

app.post("/webhook/mailemdi", express.raw({ type: "application/json" }), (req, res) => {
  const rawBody = req.body;                 // BUFFER tho, CHUA parse JSON
  const expected = "sha256=" + createHmac("sha256", process.env.MAILEMDI_WEBHOOK_SECRET)
    .update(rawBody)
    .digest("hex");
  const got = req.headers["x-webhook-signature"] || "";

  // so khop an toan thoi gian (chong timing attack)
  const ok = expected.length === got.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(got));
  if (!ok) return res.status(401).send("chu ky khong hop le");

  const payload = JSON.parse(rawBody.toString("utf8"));
  // ... xu ly su kien o day ...
  res.sendStatus(200);                       // tra 2xx that nhanh
});

Ba lỗi xác minh HMAC kinh điển: (1) ký lại trên JSON đã parse rồi stringify lại thay vì thân thô — chỉ cần lệch một dấu cách là chữ ký không khớp, nên phải dùng đúng chuỗi/buffer gốc; (2) so khớp bằng === thường thay vì hàm an toàn thời gian như timingSafeEqual; (3) quên rằng nhiều framework tự parse body nên mất bản thô — phải bật chế độ raw cho riêng route webhook.

Trả về nhanh, xử lý nặng để sau, và lo chuyện thử lại

Mailemdi thử lại tối đa 3 lần, mỗi lần chờ tối đa khoảng 5 giây nếu endpoint của bạn không phản hồi kịp hoặc trả lỗi. Từ đó rút ra hai nguyên tắc:

  • Trả mã 2xx thật nhanh. Nhận được payload hợp lệ thì lưu vào hàng đợi nội bộ rồi trả 200 ngay; đừng để việc nặng (gọi DB, gửi mail, gọi CRM) làm request treo quá vài giây kẻo bị tính là thất bại và thử lại.
  • Chuẩn bị cho trùng lặp (idempotency). Vì có cơ chế thử lại, cùng một sự kiện có thể đến hai lần. Hãy khử trùng bằng cách lưu lại dấu sự kiện đã xử lý (ví dụ kết hợp event + contactId + timestamp) và bỏ qua nếu đã thấy.

Đặc biệt chú ý sự kiện email.bounced contact.unsubscribed: hãy cập nhật ngược trạng thái trong CRM của bạn để không tiếp tục marketing tới địa chỉ đã hỏng hoặc đã từ chối. Đây cũng là cách giữ danh sách sạch — chủ đề mà bài giảm tỷ lệ bounce đào sâu hơn. Bản thân Mailemdi đã tự đưa địa chỉ hard-bounce/khiếu nại vào suppression list; webhook giúp CRM của bạn cũng biết điều đó.

4. Ví dụ một luồng tích hợp hoàn chỉnh

Gom tất cả lại thành một kịch bản thường gặp: một cửa hàng thương mại điện tử muốn website, CRM và Mailemdi phối hợp tự động quanh hành trình khách hàng.

Sơ đồ luồng dữ liệu

[Website / Form dang ky]
        |  (1) khach dien email -> backend cua ban
        v
[Backend cua ban]  --(2) POST /api/v1/contacts (X-API-Key)-->  [Mailemdi]
                                                                    |
        (3) Mailemdi gui email chao mung (automation/welcome)       |
                                                                    v
[Endpoint webhook cua ban]  <--(4) POST su kien email.opened/clicked (ky HMAC)--
        |
        |  (5) xac minh chu ky -> cap nhat diem quan tam trong CRM
        v
[CRM]  --(6) khi khach len hang VIP-> PATCH /contacts/{id} hoac POST /lists/{id}/contacts-->  [Mailemdi]
                                                                    |
        (7) khach vao phan khuc VIP -> nhan chien dich rieng        v
                                                              [Mailemdi]

Diễn giải từng bước

  1. Khách đăng ký trên website. Form gửi email về backend của bạn (không gọi thẳng Mailemdi từ trình duyệt, để giấu API key).
  2. Backend tạo liên hệ. Gọi POST /contacts kèm source: "website-form" customFields chứa mã khách CRM. Nếu nhận 409, chuyển sang PATCH để cập nhật.
  3. Mailemdi chào mừng. Một luồng automation gửi email chào mừng tự động (xem automation cho người mới).
  4. Webhook báo tương tác. Khi khách mở/click, Mailemdi bắn email.opened / email.clicked về endpoint của bạn, có chữ ký HMAC.
  5. CRM chấm điểm. Sau khi xác minh chữ ký, backend cập nhật điểm quan tâm và có thể tạo task cho sale.
  6. CRM đẩy ngược thay đổi. Khi khách lên hạng VIP, CRM gọi PATCH /contacts/{id} hoặc thêm họ vào một danh sách qua POST /lists/{id}/contacts.
  7. Phân khúc & chăm sóc. Khách rơi vào phân khúc VIP và nhận các chiến dịch riêng — vòng tròn khép lại, mọi thứ tự chạy.

Email giao dịch (transactional) — mảnh ghép thứ ba. Ngoài chiến dịch hàng loạt, bạn có thể dùng POST /transactional để gửi từng email theo sự kiện cụ thể: xác nhận đơn hàng, đặt lại mật khẩu, hóa đơn. Đây là loại email một-đối- một, kích hoạt bởi hành động của chính khách, và đi qua cùng hạ tầng đã được xác thực domain của bạn nên tỷ lệ vào hộp thư cao.

Checklist trước khi đưa tích hợp lên thật

  • Khóa nằm phía máy chủ. X-API-Key chỉ ở backend/biến môi trường, không có trong mã client. Đã có quy trình xoay khóa khi nghi lộ.
  • Có xử lý 409 (trùng). Đẩy liên hệ theo kiểu upsert: tạo mới hoặc cập nhật, không bao giờ tạo nhân đôi.
  • Tôn trọng giới hạn 120 req/phút. Có backoff khi bị từ chối; nhập kho lớn thì dùng /contacts/bulk theo lô 1000.
  • Luôn xác minh HMAC. Mọi webhook đều kiểm chữ ký bằng thân thô + so khớp an toàn thời gian; từ chối thẳng request không hợp lệ.
  • Webhook idempotent & nhanh. Trả 2xx sớm, xử lý nặng bất đồng bộ, khử trùng sự kiện lặp.
  • Đồng bộ ngược bounce/unsubscribe. Cập nhật CRM khi nhận email.bounced / contact.unsubscribed để ngừng gửi tới địa chỉ hỏng/đã từ chối.
  • Nền tảng gửi đã vững. Domain đã xác thực SPF/DKIM/DMARC trước khi bật gửi qua API — nếu không, một phần email rớt spam và mọi số liệu tích hợp đều méo.

Về điểm cuối, hãy chạy qua bài SPF, DKIM, DMARC toàn tập và kiểm tra nội dung bằng công cụ Soát từ khóa spam cùng Kiểm tra link trước khi để hệ thống tự bắn mail theo sự kiện.

Đọc thêm

  • Trợ giúp: Tích hợp API — tài liệu đầy đủ mọi nhóm endpoint, ví dụ curl và link tải đặc tả OpenAPI 3.1 để import vào Postman/Swagger.
  • Trợ giúp: Automation — dựng luồng tự động chạy sau khi liên hệ vào hệ thống qua API.
  • Phân khúc danh sách — biến dữ liệu source/customFields đẩy qua API thành các nhóm gửi đúng người.
  • Thư viện mẫu email — chọn mẫu để dùng trong email chào mừng/giao dịch mà tích hợp kích hoạt.
  • Về trang Cẩm nang — xem các bài cơ bản nếu bạn cần ôn lại nền tảng trước khi tích hợp.

Sẵn sàng nối website & CRM của bạn với Mailemdi?

Mailemdi có Public API v1 đầy đủ (xác thực X-API-Key) và webhook ký HMAC để bạn đồng bộ liên hệ hai chiều, gửi email giao dịch theo sự kiện và phản hồi hành vi khách hàng theo thời gian thực. Tạo tài khoản, lấy API key trong Cài đặt và bắt đầu tích hợp ngay.

Bắt đầu miễn phí với Mailemdi