Bài viết dành cho developer và trưởng nhóm kỹ thuật đang chuẩn bị kết nối Freshdesk với hệ thống khác như Zalo OA, phần mềm bán hàng hoặc cửa hàng online. Đọc xong, bạn sẽ biết cách xác thực, gọi các API chính, nhận webhook và xử lý những điểm quan trọng để luồng tích hợp vận hành ổn định.
Nội dung dưới đây bổ sung kinh nghiệm triển khai, không thay thế tài liệu của nhà cung cấp. Khi cần tra cứu chi tiết từng endpoint, hãy dùng Freshdesk API v2 documentation.
Tổng quan Freshdesk API v2
Freshdesk API v2 là REST API, trao đổi dữ liệu JSON qua HTTPS. Mọi request đi tới domain Freshdesk của tài khoản:
https://yourdomain.freshdesk.com/api/v2/
yourdomain là tên miền Freshdesk của tài khoản, có thể xem trên thanh địa chỉ khi đăng nhập. Tài liệu chính thức lưu ý API hoạt động qua domain Freshdesk, không qua custom CNAME.

Hình 1: Tên miền Freshdesk của tài khoản hiển thị trên thanh địa chỉ, ví dụ beehexa-support.freshdesk.com.
Các nhóm tài nguyên thường dùng khi tích hợp:
| Tài nguyên | Endpoint | Dùng để |
|---|---|---|
| Ticket | /api/v2/tickets | Tạo, cập nhật và tra cứu yêu cầu hỗ trợ |
| Conversation | /api/v2/tickets/{id}/reply, /notes | Gửi phản hồi hoặc ghi chú trên ticket |
| Contact | /api/v2/contacts | Quản lý khách hàng gửi yêu cầu |
| Company | /api/v2/companies | Quản lý khách hàng doanh nghiệp |
| Agent, Group | /api/v2/agents, /api/v2/groups | Hỗ trợ phân công và phân tuyến ticket |
| Ticket field | /api/v2/ticket_fields | Đọc cấu trúc trường, kể cả custom field |
Lấy API key Freshdesk
Freshdesk dùng API key của agent để xác thực. Quyền gọi API phụ thuộc quyền của agent sở hữu key, vì vậy nên dùng một agent dành riêng cho tích hợp thay vì tài khoản cá nhân.
Các bước lấy API key
- Đăng nhập Freshdesk bằng tài khoản agent dùng cho tích hợp.
- Bấm ảnh đại diện ở góc trên bên phải và chọn Profile settings.

Hình 2: Bấm ảnh đại diện ở góc trên bên phải và chọn Profile settings.
- Tìm khu vực Your API Key trên trang hồ sơ. Nếu key đang ẩn, bấm View API key và hoàn tất bước xác minh nếu hệ thống yêu cầu.
- Sao chép API key và lưu trong kho bí mật của hệ thống.

Hình 3: Khu vực Your API Key trong trang hồ sơ; API key và thông tin cá nhân đã được che.
Nếu dùng Reset API Key, key cũ sẽ không còn dùng được. Mọi ứng dụng đang giữ key cũ phải được cập nhật trước khi hoạt động trở lại.
Gọi API với API key
Freshdesk dùng Basic Auth: username là API key, còn password có thể là một chuỗi bất kỳ như X.
curl -u YOUR_API_KEY:X \
-H "Content-Type: application/json" \
https://yourdomain.freshdesk.com/api/v2/tickets
Một vài nguyên tắc nên áp dụng:
- Dùng agent riêng cho tích hợp: giảm phụ thuộc vào vòng đời tài khoản nhân sự và giúp kiểm soát quyền.
- Cấp quyền vừa đủ: agent tích hợp chỉ nên truy cập các group và tài nguyên mà luồng dữ liệu thực sự sử dụng.
- Không lưu key trong mã nguồn: đặt API key trong biến môi trường hoặc kho bí mật; không gửi key vào log.
Rate limit và cách tránh lỗi 429
Freshdesk giới hạn số lượt gọi API theo phút, tính trên toàn tài khoản, không phụ thuộc số agent hay địa chỉ IP. Bảng giới hạn hiện được tài liệu Freshdesk công bố như sau:
| Gói | Tổng lượt gọi/phút | Tạo ticket | Cập nhật ticket | Liệt kê ticket | Liệt kê contact |
|---|---|---|---|---|---|
| Free | 0 | 0 | 0 | 0 | 0 |
| Growth | 100 | 50 | 50 | 40 | 40 |
| Pro | 400 | 160 | 160 | 100 | 100 |
| Enterprise | 700 | 280 | 280 | 200 | 200 |
| Trial | 50 | — | — | — | — |
Freshdesk cho biết cơ chế giới hạn theo phút được áp dụng theo từng đợt; một số tài khoản cũ có thể vẫn dùng giới hạn theo giờ. Vì vậy, không nên cố định duy nhất các con số trên trong code. Hãy kiểm tra gói hiện tại và đọc header thực tế của response.
Các header cần theo dõi:
X-Ratelimit-Total: tổng số lượt được phép trong cửa sổ hiện tại;X-Ratelimit-Remaining: số lượt còn lại;X-Ratelimit-Used-CurrentRequest: số lượt request vừa rồi tiêu thụ.
Khi vượt giới hạn, API trả về 429 Too Many Requests cùng header Retry-After, tính bằng giây. Cách xử lý nên áp dụng:
- Chờ đúng
Retry-Afterrồi mới gửi lại, không retry ngay lập tức. - Đưa request vào hàng đợi thay vì gọi song song không giới hạn, nhất là khi đồng bộ dữ liệu lớn lần đầu.
- Hạn chế
includehoặc embed không cần thiết: mỗi tài nguyên được nhúng có thể tiêu thụ thêm lượt gọi. - Cache dữ liệu ít thay đổi: chẳng hạn ánh xạ tên agent với agent ID.
API Pagination and Incremental Data Pulling
Các API liệt kê trả về 30 bản ghi mỗi trang theo mặc định, tối đa 100 khi đặt per_page. Giá trị lớn hơn 100 bị coi là không hợp lệ.
GET /api/v2/tickets?per_page=100&page=2
Nếu còn trang tiếp theo, response có header link chứa URL với rel="next". Hãy đọc header này thay vì suy đoán tổng số trang.
Để đồng bộ tăng dần, chỉ lấy các ticket có hoạt động kể từ lần chạy trước:
GET /api/v2/tickets?updated_since=2026-10-01T00:00:00Z&per_page=100
Thời điểm trong request nên dùng UTC và lần chạy kế tiếp nên có một khoảng chồng lấn nhỏ để giảm nguy cơ bỏ sót bản ghi ở ranh giới. Middleware cần chống xử lý trùng dựa trên ticket ID và updated_at.
Khi cần lọc theo điều kiện phức tạp hơn, dùng Search API. Kết quả trả 30 bản ghi mỗi trang, tối đa 10 trang; câu truy vấn dài tối đa 512 ký tự, phải đặt trong dấu nháy kép và URL encode.
GET /api/v2/search/tickets?query="status:2%20AND%20priority:3"
Search API có độ trễ lập chỉ mục vài phút, vì vậy không nên dùng nó như nguồn duy nhất cho một luồng đồng bộ đòi hỏi nhận thay đổi ngay sau khi ghi.
Các đối tượng chính và cách ánh xạ dữ liệu
Phần lớn công sức tích hợp nằm ở việc thống nhất dữ liệu bên ngoài tương ứng với đối tượng và trường nào trong Freshdesk.
| Đối tượng | Dữ liệu bên ngoài thường gặp | Ghi chú ánh xạ |
|---|---|---|
| Ticket | Tin nhắn Zalo OA, yêu cầu đổi trả, khiếu nại đơn hàng | Lưu mã tham chiếu bên ngoài vào custom field |
| Contact | Khách hàng trên Zalo, phần mềm bán hàng, cửa hàng online | Khớp theo email hoặc số điện thoại và xử lý trùng trước khi tạo |
| Company | Khách hàng doanh nghiệp | Dùng khóa nghiệp vụ ổn định như mã khách hàng; không chỉ dựa vào tên |
| Conversation | Tin nhắn trả lời, ghi chú nội bộ | Lưu ID tin nhắn bên ngoài để tránh ghi trùng |
| Group | Nhóm phụ trách theo kênh hoặc khu vực | Ánh xạ từng kênh tiếp nhận về đúng group |
| Custom field | Mã đơn, kênh, mã khách | Tạo và kiểm thử trước khi go-live; tên API thường có tiền tố cf_ |
Các giá trị mặc định thường gặp khi tạo ticket:
- status:
2Open,3Pending,4Resolved,5Closed; - priority:
1Low,2Medium,3High,4Urgent; - source:
1Email,2Portal,3Phone,7Chat,9Feedback Widget,10Outbound Email.
Tài khoản có thể được cấu hình thêm trường hoặc trạng thái. Trước khi đưa vào vận hành, hãy đọc /api/v2/ticket_fields và xác nhận giá trị thực tế thay vì chỉ dựa vào danh sách mặc định.
Khi tạo ticket, cần cung cấp thông tin để xác định requester, chẳng hạn requester_id, email, phone, facebook_id hoặc twitter_id. Một số lựa chọn có ràng buộc bổ sung; ví dụ nếu chỉ gửi phone mà không có email thì trường name cũng bắt buộc.
Ví dụ code Freshdesk API
Tạo ticket kèm custom field
curl -u YOUR_API_KEY:X \
-H "Content-Type: application/json" \
-X POST \
-d '{
"email": "khachhang@example.com",
"subject": "Yêu cầu đổi size đơn #10234",
"description": "Khách muốn đổi sang size M.",
"status": 2,
"priority": 2,
"source": 7,
"custom_fields": { "cf_ma_don_hang": "10234" }
}' \
https://yourdomain.freshdesk.com/api/v2/tickets
Tên cf_ma_don_hang chỉ là ví dụ. Custom field phải tồn tại trong tài khoản trước khi request được gửi.
Tìm hoặc tạo contact theo email bằng Node.js
const BASE = 'https://yourdomain.freshdesk.com/api/v2';
const AUTH = `Basic ${Buffer.from(
`${process.env.FRESHDESK_API_KEY}:X`,
).toString('base64')}`;
async function freshdeskRequest(url, options = {}) {
const response = await fetch(url, {
...options,
headers: {
Authorization: AUTH,
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const body = await response.text();
throw new Error(`Freshdesk ${response.status}: ${body}`);
}
return response.json();
}
async function findOrCreateContact(email, name) {
const contacts = await freshdeskRequest(
`${BASE}/contacts?email=${encodeURIComponent(email)}`,
);
if (contacts.length > 0) return contacts[0];
return freshdeskRequest(`${BASE}/contacts`, {
method: 'POST',
body: JSON.stringify({ email, name }),
});
}
Trong môi trường có nhiều worker, hai tiến trình vẫn có thể cùng không tìm thấy contact rồi cùng tạo mới. Hãy xử lý response xung đột và dùng khóa hoặc bảng ánh xạ ở lớp trung gian nếu cần bảo đảm duy nhất.
Duyệt ticket đã cập nhật và xử lý 429
async function fetchUpdatedTickets(since) {
const headers = { Authorization: AUTH };
let url = `${BASE}/tickets?updated_since=${encodeURIComponent(since)}&per_page=100`;
const tickets = [];
while (url) {
const response = await fetch(url, { headers });
if (response.status === 429) {
const waitSeconds = Number(response.headers.get('Retry-After') || 60);
await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
continue;
}
if (!response.ok) {
throw new Error(`Freshdesk ${response.status}: ${await response.text()}`);
}
tickets.push(...await response.json());
const link = response.headers.get('link');
const next = link?.match(/<([^>]+)>;\s*rel="next"/);
url = next ? next[1] : null;
}
return tickets;
}
Ví dụ trên minh họa cơ chế chính. Khi chạy production, nên bổ sung timeout, retry có jitter cho lỗi mạng và 5xx, logging có correlation ID và giới hạn tổng thời gian xử lý.
Tạo webhook bằng Automation rule
Với ticket, Freshdesk gửi webhook thông qua action Trigger Webhook trong Automation rules khi ticket thỏa điều kiện đã cấu hình. Ví dụ dưới đây tạo rule gửi webhook khi agent thêm public note vào ticket.

Hình 4: Mở Admin › Workflows › Automations trong Freshdesk.
Bước 1: Mở trang Automations
Trong Freshdesk, vào Admin › Workflows › Automations. Với Freshdesk Omni, đường dẫn có thể là Admin Settings › Configuration and Workflows › Ticket Automations. Chọn tab phù hợp:
- Ticket Creation: chạy khi có ticket mới;
- Ticket Updates: chạy khi ticket thay đổi, chẳng hạn có phản hồi hoặc ghi chú mới;
- Hourly Triggers: chạy theo lịch và điều kiện thời gian.
Bấm New Rule để tạo rule mới.

Hình 5: Chọn tab Ticket Updates và bấm New Rule.
Bước 2: Đặt tên, chọn sự kiện và điều kiện
Một rule cho ticket update gồm Event, Condition và Action:
- Tên rule: đặt tên dễ truy vết, ví dụ
HexaSync - Public note webhook. - Event: chọn người thực hiện và sự kiện kích hoạt, chẳng hạn agent thêm public note.
- Condition: lọc thêm theo thuộc tính ticket; chọn Match ANY hoặc Match ALL phù hợp với nghiệp vụ.

Hình 6: Thêm action Trigger Webhook sau khi thiết lập event và condition.
Bước 3: Chọn action Trigger Webhook
Ở phần Action, chọn Trigger Webhook và cấu hình:
| Trường | Cách cấu hình gợi ý |
|---|---|
| Request type | Dùng POST khi gửi sự kiện sang hệ thống nhận |
| URL | URL HTTPS của endpoint nhận; có thể chèn placeholder như {{ticket.id}} |
| Requires authentication | Bật nếu endpoint cần xác thực và chọn phương thức phù hợp |
| Add custom headers | Thêm header phục vụ xác thực hoặc versioning, không ghi bí mật vào URL |
| Encoding | Chọn JSON |
| Content | Dùng Simple để chọn trường có sẵn hoặc Advanced để tự viết body |
Với Simple, có thể chọn Ticket ID, Subject, Last Public Comment, Agent Name hoặc Contact Name từ danh sách placeholder mà giao diện cung cấp.

Hình 7: Chọn JSON, cấu hình nội dung và kiểm tra các placeholder trước khi preview.
Nếu cần cấu trúc JSON riêng, chọn Advanced và dùng đúng placeholder hiển thị trong tài khoản, ví dụ:
{
"ticket_id": "{{ticket.id}}",
"subject": "{{ticket.subject}}",
"last_public_comment": "{{ticket.latest_public_comment}}",
"contact_name": "{{ticket.contact.name}}"
}
Bước 4: Xem trước, lưu và kiểm tra
Bấm Preview để xem lại rule rồi lưu. Thêm một public note vào ticket thử và kiểm tra endpoint có nhận đúng request, header và body hay không. Không dùng dữ liệu khách hàng thật cho lần thử đầu tiên nếu môi trường nhận chưa được kiểm soát.
Giới hạn và cơ chế gửi lại
Theo tài liệu hỗ trợ của Freshdesk:
- tối đa 1.000 lượt gọi webhook mỗi giờ;
- mã 200–299 được coi là thành công;
- mã 300–399 được chuyển hướng;
- các trường hợp khác được coi là thất bại và Freshdesk tự gửi lại mỗi 30 phút, tổng cộng 48 lần;
- lượt gọi vượt giới hạn được đưa vào bộ đệm cho đến khi có hạn mức mới.
Xử lý ở phía nhận webhook
- Xác thực request: kiểm tra credential hoặc header bí mật đã cấu hình và chỉ nhận qua HTTPS.
- Phản hồi nhanh: xác thực tối thiểu, ghi sự kiện bền vững rồi trả mã 2xx; đưa việc xử lý nặng vào hàng đợi.
- Chịu được gửi lặp: cùng một sự kiện có thể đến nhiều lần do retry, nên dùng khóa idempotency nội bộ dựa trên dữ liệu sự kiện.
- Không tin tuyệt đối vào payload: kiểm tra kiểu dữ liệu, giới hạn kích thước và escape nội dung trước khi ghi log hoặc hiển thị.
- Lấy dữ liệu đầy đủ khi cần: webhook chỉ nên mang mã ticket và các trường chính; dữ liệu còn lại có thể đọc lại qua API.
Kinh nghiệm từ dự án thực tế
Chống tạo ticket trùng
Freshdesk API không công bố idempotency key cho thao tác tạo ticket. Khi client retry hoặc hệ thống nguồn gửi lặp, cùng một yêu cầu có thể tạo nhiều ticket. Cách an toàn là giữ bảng ánh xạ tại middleware giữa mã bên ngoài và ticket ID Freshdesk, tra cứu trước khi tạo, đồng thời lưu mã bên ngoài vào custom field để đối soát.
Nếu luồng có xử lý song song, chỉ thao tác “tra cứu rồi tạo” là chưa đủ. Cần unique constraint hoặc cơ chế khóa ở lớp dữ liệu để hai worker không cùng tạo bản ghi.
Retry có kiểm soát
- Lỗi 429: chờ theo
Retry-After. - Lỗi 5xx hoặc lỗi mạng: retry với exponential backoff có jitter và giới hạn số lần.
- Lỗi 4xx khác: không retry tự động theo cùng một payload; ghi lại lỗi để sửa quyền, dữ liệu hoặc ánh xạ.
- Bản ghi thất bại nhiều lần: chuyển vào dead-letter queue để xử lý lại sau, không chặn toàn bộ luồng.
Đọc đúng mã lỗi
| Mã | Ý nghĩa thường gặp | Cách xử lý |
|---|---|---|
| 400 | Thiếu trường bắt buộc, sai kiểu hoặc sai giá trị | Đọc mảng errors để sửa ánh xạ hoặc payload |
| 401 | Sai hoặc thiếu API key | Kiểm tra key và cách mã hóa Basic Auth |
| 403 | Agent không đủ quyền hoặc tính năng không khả dụng | Kiểm tra quyền agent, gói và cấu hình tài khoản |
| 404 | Không tìm thấy tài nguyên | Kiểm tra ID, trạng thái xóa và bảng ánh xạ |
| 409 | Trạng thái xung đột, chẳng hạn contact đã tồn tại | Đọc lỗi và đối soát bản ghi hiện có |
| 429 | Vượt rate limit | Chờ Retry-After và giảm tốc độ gửi |
Thời gian và trạng thái
- Freshdesk trả timestamp theo UTC. Lưu dữ liệu ở UTC và chỉ quy đổi sang giờ Việt Nam ở lớp hiển thị.
- Dùng timestamp có timezone rõ ràng; không trộn thời gian local không offset với UTC.
- Trạng thái đơn hàng hoặc hội thoại ở hệ thống khác cần được ánh xạ rõ sang status của ticket, kể cả ngoại lệ như hoàn trả một phần.
Không muốn tự xây kết nối?
HexaSync cung cấp giải pháp tích hợp Freshdesk với Zalo OA và có thể khảo sát nhu cầu kết nối Sapo, KiotViet, Shopify, TikTok Shop, WooCommerce, Magento, SHOPLINE, LarkSuite, Slack, Google Sheets hoặc Microsoft 365 Excel. Phạm vi cụ thể phụ thuộc phiên bản, gói dịch vụ, quyền API, dữ liệu và quy tắc nghiệp vụ của từng doanh nghiệp.
Liên hệ HexaSync để đánh giá miễn phí yêu cầu kết nối và xác định phạm vi triển khai.
Câu hỏi thường gặp
Tài liệu Freshdesk API chính thức ở đâu?
Tài liệu Freshdesk API v2 nằm tại Freshdesk Developers, gồm mô tả từng endpoint, tham số, mã lỗi và ví dụ request.
Freshdesk API có miễn phí không?
Theo bảng giới hạn API của Freshdesk, gói Free có giới hạn 0 lượt gọi mỗi phút; Growth, Pro, Enterprise và tài khoản trial có giới hạn riêng. Hãy kiểm tra gói và response header của chính tài khoản trước khi triển khai.
Lấy API key Freshdesk ở đâu?
Bấm ảnh đại diện ở góc trên bên phải, chọn Profile settings. API key nằm trong khu vực Your API Key trên trang hồ sơ.
Freshdesk có webhook không?
Có. Với ticket, bạn có thể cấu hình action Trigger Webhook trong Admin › Workflows › Automations, sau đó đặt điều kiện kích hoạt, URL nhận và nội dung gửi đi.
Làm sao tránh lỗi 429 khi đồng bộ nhiều dữ liệu?
Theo dõi các header X-Ratelimit-*, đưa request vào hàng đợi, chờ theo Retry-After khi gặp 429 và hạn chế nhúng tài nguyên không cần thiết trong response.



