Agent API Docs
Bản cho máy đọc: /docs/api.txt (text/markdown thuần).
Auth
Mọi endpoint /api/agent/* yêu cầu header 'Authorization: Bearer <API_KEY>' (hoặc 'x-agent-key: <API_KEY>'). API key tạo ở trang /admin/api-keys, chỉ hiện một lần lúc tạo. Sai/thiếu key trả 401, server chưa cấu hình key trả 503.
Authorization: Bearer <API_KEY>Quy chuẩn SEO khi POST/PATCH bài
- title: 1 dòng, 3-300 ký tự (khuyến nghị ≤ 70 để không bị cắt trong SERP). API tự gộp whitespace/newline.
- excerpt: 1-2 câu, tối đa 160 ký tự, 1 dòng. Gửi thiếu thì API tự suy ra từ content. Đây là meta description của trang bài.
- content: HTML an toàn, CHỈ dùng thẻ <p>, <h2>, <h3>, <ul>, <ol>, <li>, <strong>, <em>, <blockquote>. 3-5 đoạn, 350-550 từ. Không markdown, không link, không script, không ảnh inline, không inline style. Mọi thứ khác bị sanitize lúc render.
- imageUrl: URL http(s). Ưu tiên ảnh upload qua POST /api/agent/media (URL nội bộ), không hotlink bừa từ site khác.
- category: một trong tech, ai, startup, dev, security, mobile, gadget, other. Sai sẽ fallback về tech.
- publishedAt: ISO datetime, thiếu thì lấy thời điểm hiện tại.
- isHot / isHidden: boolean. isHidden=false là published, true là draft/ẩn.
- Mọi bài tạo/sửa qua API đều được áp dụng các chuẩn trên tự động (title/excerpt 1 dòng, suy excerpt, clamp meta description ~165 ký tự).
Endpoints
/api/agent/statsThống kê tổng quan: số bài, ẩn, hot, nguồn active, nguồn lỗi, 10 log crawl gần nhất.
curl https://tonghop.io.vn/api/agent/stats -H 'Authorization: Bearer <API_KEY>'Response: { "articles": 72, "hidden": 0, "hot": 0, "sources": 1, "sourceErrors": 0, "latestLogs": [...] }
/api/agent/articlesList/tìm bài viết, mới nhất trước.
Tham số: q (tìm trong title/excerpt/url), category (enum), hidden (1|0 hoặc true|false, không gửi = tất cả), page (≥1), limit (1-50, mặc định 20).
curl 'https://tonghop.io.vn/api/agent/articles?q=ai&category=tech&hidden=0&page=1&limit=20' -H 'Authorization: Bearer <API_KEY>'Response: { "total": 192, "page": 1, "limit": 20, "items": [Article...] } — Article kèm source { name, slug }.
/api/agent/articles/:idLấy 1 bài. :id nhận cả article id hoặc slug.
curl https://tonghop.io.vn/api/agent/articles/ten-bai-viet-slug -H 'Authorization: Bearer <API_KEY>'Response: Article object (404 nếu không có).
/api/agent/articlesĐăng bài mới (link-out hoặc full content viết lại). Tuân thủ Quy chuẩn SEO ở trên.
Body: { "title": string (bắt buộc), "url": string http(s) (bắt buộc, link bài gốc), "excerpt"?: string, "content"?: string HTML, "imageUrl"?: string, "author"?: string, "category"?: enum, "publishedAt"?: ISO, "isHot"?: boolean, "isHidden"?: boolean, "sourceId"?: string, "sourceSlug"?: string (mặc định "tong-hop") }
curl -X POST https://tonghop.io.vn/api/agent/articles -H "Authorization: Bearer <API_KEY>" -H "Content-Type: application/json" \
-d '{"title":"Tiêu đề bài","url":"https://nguon.com/bai","excerpt":"Tóm tắt dưới 160 ký tự.","content":"<p>Nội dung HTML...</p>","imageUrl":"https://tonghop.io.vn/uploads/media/xxx.jpg","category":"tech"}'Response: 201 { "ok": true, "article": {...} }. 400 nếu sai body/url.
/api/agent/articles/:idSửa bài (partial — gửi field nào sửa field đó). :id nhận id hoặc slug.
Body: Các field như POST, thêm: "regenerateSlug"?: boolean (tạo slug mới từ title). "imageUrl": null để XÓA ảnh bìa.
curl -X PATCH https://tonghop.io.vn/api/agent/articles/ten-bai-slug -H "Authorization: Bearer <API_KEY>" -H "Content-Type: application/json" \
-d '{"title":"Tiêu đề mới","isHot":true}'Response: { "ok": true, "article": {...} }. 404 nếu không tìm thấy.
/api/agent/articles/:idXóa vĩnh viễn 1 bài. :id nhận id hoặc slug.
curl -X DELETE https://tonghop.io.vn/api/agent/articles/ten-bai-slug -H 'Authorization: Bearer <API_KEY>'Response: { "ok": true, "deleted": "<id>" }
/api/agent/moderateKiểm duyệt hàng loạt theo danh sách ids.
Body: { "ids": string[1..100], "action": "hide" | "show" | "hot" | "unhot" | "delete" }
curl -X POST https://tonghop.io.vn/api/agent/moderate -H "Authorization: Bearer <API_KEY>" -H "Content-Type: application/json" \
-d '{"ids":["id1","id2"],"action":"hide"}'Response: { "ok": true, "action": "hide", "count": 2 }
/api/agent/sourcesList toàn bộ nguồn RSS.
Response: { "items": [Source...] }
/api/agent/sourcesThêm nguồn RSS mới (slug tự sinh từ name, trùng thì thêm -1, -2...).
Body: { "name": string, "rssUrl": string, "siteUrl": string, "category"?: enum, "lang"?: string, "isActive"?: boolean }
Response: 201 { "ok": true, "source": {...} }
/api/agent/fetchChạy crawl RSS toàn bộ nguồn active ngay lập tức (không đợi cron 20 phút).
Response: { "ok": true, "sources": n, "itemsNew": n, "errors": n }
/api/agent/mediaUpload ảnh bìa, trả URL nội bộ để dùng cho article.imageUrl. Giới hạn 5MB, nhận jpg/png/gif/webp/avif (check magic bytes).
Body: multipart/form-data, field 'file'
curl -X POST https://tonghop.io.vn/api/agent/media -H "Authorization: Bearer <API_KEY>" -F "file=@cover.jpg"Response: { "ok": true, "url": "https://tonghop.io.vn/uploads/media/...", "imageUrl": "..." }
/api/cron/fetch và /api/cron/hotwriteCron nội bộ (Bearer CRON_SECRET, không dùng API key): fetch = crawl RSS; hotwrite = tìm tin hot + AI viết lại (cần OMNIROUTE_API_KEY).
curl https://tonghop.io.vn/api/cron/fetch -H 'Authorization: Bearer <CRON_SECRET>'Lỗi chung
401 sai/thiếu key — 403 sai origin (admin) — 400 body lỗi — 413 file >5MB — 415 sai loại ảnh — 503 server chưa cấu hình.