# Bắt đầu với TinoPage Builder

TinoPage Builder dựng website cho cơ sở kinh doanh bằng AI: khách kể về cơ sở, AI chọn mẫu và viết nội dung, khách sửa tiếp trong trình sửa của TinoPage Builder. Website nhận liên hệ, đơn hàng và chạy trên tên miền riêng. API của dịch vụ cho ứng dụng của bạn xem hạn mức, tạo website bằng AI, đăng, tạm ngưng, xoá website, gắn tên miền riêng, xem liên hệ, đơn hàng và mở TinoPage Builder bằng vé đăng nhập một lần.

Cập nhật: 2026-10-26. Nguồn: module TinoPage Builder của Tino và API của TinoPage Builder.

## Gọi API

| Mục | Giá trị |
| --- | --- |
| Địa chỉ | `https://api.tino.vn/service/<id dịch vụ>/builder/…` |
| Xác thực | `Authorization: Bearer <token>`: token đăng nhập của chủ dịch vụ TinoPage Builder; qua `/v1`: token ứng dụng OAuth hoặc token API cá nhân có quyền `builder:*` |
| Body | JSON, `Content-Type: application/json` |
| Từng route | mục TinoPage Builder ở [API Reference](/docs/reference#tinopage-builder) |

- Chỉ các tham số ghi ở từng route được chuyển tiếp. Tino kiểm dạng trước khi gọi TinoPage Builder: ID website không phải UUID, tên miền sai dạng, query hay body lạ trả 400 `INVALID_REQUEST`, `details.field` chỉ trường.
- ID website trong đường dẫn là UUID; tên miền trong đường dẫn viết chữ thường.
- Sửa nội dung website (trang, khối, ảnh, mặt hàng) và xử lý liên hệ, đơn hàng làm trong TinoPage Builder: mở bằng [vé đăng nhập](#ve-dang-nhap).

Từ 27/10/2026 các route TinoPage Builder cũng gọi được qua gateway `/v1`: `https://api.tino.vn/v1/service/<id dịch vụ>/builder/…` với token ứng dụng OAuth (`tinoat_`, quyền `builder:read`, `builder:write`) hoặc [token API cá nhân](/docs/personal-tokens) (`tinopat_`), cùng body và câu trả lời. Riêng [vé đăng nhập](#ve-dang-nhap) (`POST …/builder/login`) không mở qua `/v1`: chỉ token đăng nhập lấy được.

Token đăng nhập là token khách dùng ở khu vực khách hàng tino.vn; [SDK](/docs/sdk) lấy được bằng đăng nhập mã OTP:

```python
from tino_userapi import TinoClient

client = TinoClient()
client.request_login_otp("you@example.com")
client.verify_login_otp("you@example.com", input("Mã OTP: "))
print(client.request("GET", "/service/100003/builder/overview"))
```

`100003` là ID dịch vụ TinoPage Builder: cách tìm ở mục dưới.

## Tìm dịch vụ TinoPage Builder

Mỗi khách Tino có một tài khoản TinoPage Builder, gắn với dịch vụ TinoPage Builder đã mua.

1. Lấy ứng viên ở [Danh sách dịch vụ](/docs/api/get-service) (`product_type`) hoặc [Chi tiết dịch vụ](/docs/api/get-service-id) (`ptype`): giá trị `tinobuilderpanel`. Dịch vụ không có giá trị này thì xem `acc_module`: `TinoPage Builder`. So không phân biệt hoa thường, bỏ khoảng trắng, `_`, `-`.
2. Gọi [Tổng quan tài khoản](/docs/api/builder-get-overview) (`GET /service/<id>/builder/overview`):

| Kết quả | Nghĩa |
| --- | --- |
| 200 | dịch vụ TinoPage Builder của khách: gọi các route khác với ID này |
| 404 `SERVICE_NOT_LINKED` | tài khoản TinoPage Builder đã gắn với dịch vụ khác của khách: thử dịch vụ kế tiếp |
| 404 `SERVICE_NOT_FOUND`, `invalid method` | không phải dịch vụ TinoPage Builder của tài khoản đang đăng nhập |
| 404 `ACCOUNT_NOT_FOUND` | chưa có tài khoản TinoPage Builder cho dịch vụ: liên hệ hỗ trợ Tino |

## Hạn mức theo dịch vụ

TinoPage Builder không bán theo gói cố định: hạn mức theo dịch vụ đã mua, nằm ở `config.effective` của [Tổng quan tài khoản](/docs/api/builder-get-overview). Đọc hạn mức từ API, đừng ghi cứng: số có thể đổi khi dịch vụ đổi. `-1` là không giới hạn.

| Khoá | Áp cho | Nghĩa |
| --- | --- | --- |
| `sites` | tài khoản | số website (chưa xoá, kể cả nháp) |
| `pages_per_site` | mỗi website | số trang |
| `items_per_site` | mỗi website | số mặt hàng |
| `domains_per_site` | mỗi website | số tên miền riêng |
| `ai_generations_daily` | mỗi website | lượt AI tạo, sửa nội dung trong 24 giờ |
| `ai_chat_daily` | mỗi website | tin nhắn với trợ lý AI trong 24 giờ |
| `ai_images_daily` | mỗi website | ảnh AI trong 24 giờ |

- Số đang dùng: `usage` của Tổng quan tài khoản (`sites`, `sites_paused`, `sites_published`) và `pages`, `items`, `domains` (`used`, `limit`) của từng website.
- Hết số website: tạo website trả 409 `LIMIT_EXCEEDED`, `details.key` là `sites`; hết số tên miền: `domains_per_site`.
- Sau khi hạ hạn mức hoặc dịch vụ chấm dứt, tài khoản giữ chạy các website cũ nhất trong số `sites`; website còn lại bị hệ thống tạm ngưng (`paused: true`, `paused_reason: over_limit`): trang công khai báo website tạm ngưng, không nhận liên hệ, đơn hàng. Nâng hạn mức hoặc xoá bớt website thì website tạm ngưng chạy lại, cũ nhất trước. Trang, mặt hàng, tên miền vượt hạn mức không bị xoá, chỉ không thêm mới được.
- Dịch vụ tạm ngưng: chỉ đọc. Thao tác ghi trả 403 `ACCOUNT_SUSPENDED`; `GET` và vé đăng nhập vẫn dùng được (vào chỉ xem). Dịch vụ đã chấm dứt: thao tác ghi và vé đăng nhập trả 403 `ACCOUNT_TERMINATED`.

## Tạo website bằng AI

[Tạo website bằng AI](/docs/api/builder-post-sites) nhận `intake`: thông tin cơ sở kinh doanh để AI chọn mẫu và viết nội dung.

| Ô | Bắt buộc | Tối đa (ký tự) | Ghi chú |
| --- | --- | --- | --- |
| `business_name` | có | 120 | tên cơ sở |
| `industry` | có | 80 | ngành nghề, vd tiệm bánh, spa, quán cà phê |
| `phone` | có | 20 | số Việt Nam: đầu `0`, `84` hoặc `+84`, rồi 9 số di động (bắt đầu 3, 5, 7, 8, 9) hoặc 10 số cố định (bắt đầu 2); được có khoảng trắng, chấm, gạch, ngoặc |
| `address` | — | 200 | địa chỉ |
| `city` | — | 80 | tỉnh, thành phố |
| `services` | — | 300 | dịch vụ, sản phẩm chính |
| `hours` | — | 100 | giờ mở cửa |
| `zalo` | — | 100 | số điện thoại hoặc đường dẫn Zalo |
| `email` | — | 120 | email liên hệ |

- Ô ngoài bảng, ô không phải chuỗi hoặc quá dài trả 400 `INVALID_REQUEST` (`details.field`: `intake` hoặc `intake.<ô>`). Thiếu ô bắt buộc, số điện thoại hay email sai trả 422 `VALIDATION_FAILED`, `details` theo `intake.<ô>` kèm lý do tiếng Việt.
- Lượt tạo chạy nền 1–5 phút: lệnh tạo trả HTTP 202 với `state: running`. Hỏi [Tiến độ tạo website](/docs/api/builder-get-site-creation) vài giây một lần tới khi `state` là `done` (`site_id` là website mới) hoặc `failed` (`message` là lý do tiếng Việt).
- Mỗi tài khoản một lượt tạo tại một thời điểm: lượt thứ hai trả 409 `CREATION_IN_PROGRESS`. Tính năng AI tạm dừng trả 503 `AI_UNAVAILABLE`.
- Kết quả lượt tạo giữ 1 giờ; sau đó (hoặc khi máy chủ khởi động lại) Tiến độ tạo website trả `idle`, website đã tạo vẫn có ở [Danh sách website](/docs/api/builder-get-sites).
- Tiến độ có thể có thêm `step` (`queued`, `design`, `write`, `images`, `build`) và `mode` (`design`, `merge`) khi xong: chỉ để hiện tiến độ, đừng rẽ nhánh theo hai khoá này.

## Đăng, tạm ngưng, xoá website

| Trạng thái | Nghĩa |
| --- | --- |
| `status: draft` | chưa đăng: website mới tạo, hoặc đã tạm ngưng bằng [Tạm ngưng website](/docs/api/builder-post-sites-site-unpublish) |
| `status: published` | đã đăng, chạy trên `host` (tên miền riêng) hoặc link xem thử `preview_url` |
| `paused: true` | hệ thống tạm ngưng (`paused_reason: over_limit`: tài khoản vượt số website), đứng trên `status` |

- [Đăng website](/docs/api/builder-post-sites-site-publish) đăng bản mới nhất, cũng là mở lại website đã tạm ngưng. Website `paused` trả 409 `SITE_PAUSED`: xoá bớt website khác hoặc nâng số website.
- [Tạm ngưng website](/docs/api/builder-post-sites-site-unpublish) gỡ website khỏi mạng, nội dung giữ nguyên.
- [Xoá website](/docs/api/builder-delete-sites-site) đưa website vào thùng rác (Tino khôi phục được theo yêu cầu qua hỗ trợ), gỡ tên miền riêng của website và trả lại một chỗ trong số website.
- [Chi tiết website](/docs/api/builder-get-sites-site) có `todo`: số liên hệ chưa gọi lại (`leads_new`) và đơn hàng chưa xác nhận (`orders_new`).

## Tên miền riêng

1. Đăng website trước: website chưa đăng trả 409 `SITE_NOT_PUBLISHED`.
2. [Gắn tên miền riêng](/docs/api/builder-post-sites-site-domains) với `hostname` (vd `example.vn`, `banhang.example.vn`): trả 201, tên miền ở `status: pending` kèm `dns`, các bản ghi khách cần đặt ở nơi quản lý DNS của tên miền.
3. Khách đặt bản ghi: CNAME về `dns_target` (tên `@` và `www` với tên miền gốc, nhãn con với tên miền con) và TXT `_tino-verify` (giá trị `tino-site-verify=…`) chứng minh sở hữu. Tên bản ghi tính từ tên miền gốc. Lấy `dns_target` và giá trị TXT từ API, đừng ghi cứng.
4. TinoPage Builder tự kiểm lại tên miền `pending` theo lịch (`next_check_at`); [Kiểm tra lại DNS](/docs/api/builder-post-sites-site-domains-hostname-verify) kiểm ngay. Đủ CNAME và TXT thì `status` chuyển `active`, chứng chỉ HTTPS được cấp tự động (`ssl_status`: `pending`, `active`, `error`); `www` chuyển về tên miền gốc.

- `cname_ok`, `txt_ok` là kết quả lần kiểm gần nhất; `error` nói thiếu gì (tiếng Việt), rỗng khi đạt. `checked_at` là `0001-01-01T00:00:00Z` khi chưa kiểm lần nào.
- `owner_verified: true`: chủ tên miền đã được xác nhận, không cần TXT (`dns` chỉ còn CNAME).
- Tự kiểm dừng sau 14 lần không đạt (khoảng 10 ngày); `error` ghi rõ, gọi Kiểm tra lại DNS để thử tiếp.
- Hết số tên miền của website: 409 `LIMIT_EXCEEDED` (`details.key: domains_per_site`); tên miền đang gắn website khác: 409 `DOMAIN_TAKEN`; không phải tên miền: 422 `VALIDATION_FAILED` (`details.hostname`).

## Liên hệ và đơn hàng

[Liên hệ và đơn hàng](/docs/api/builder-get-sites-site-leads) chỉ đọc: `kind=lead` (mặc định) là liên hệ từ biểu mẫu của website, `kind=order` là đơn hàng; mới trước, phân trang bằng `page`, `per_page` (mặc định 20, tối đa 50) kèm `meta`.

| `status` | Nghĩa | Lọc được |
| --- | --- | --- |
| `moi` | mới | có |
| `da_lien_he` | đã liên hệ (liên hệ) | có |
| `xac_nhan` | đã xác nhận | có |
| `dang_lam` | đang làm (đơn món) | không |
| `san_sang` | sẵn sàng (đơn món) | không |
| `dang_giao` | đang giao | có |
| `hoan_tat` | hoàn tất | có |
| `huy` | đã huỷ | có |

- Liên hệ: `id`, `kind`, `name`, `phone`, `payload` (nội dung biểu mẫu, khoá tuỳ mẫu website), `source_page`, `status`, `created_at`.
- Đơn hàng: `id`, `code`, `name`, `phone`, `address`, `note`, `items` (`name`, `option`, `qty`, `subtotal`), `shipping`, `total` (`null` khi website không ghi giá), `payment` (`COD`, `Chuyển khoản` hoặc chữ khác), `status`, `status_label` (nhãn tiếng Việt), `created_at`, `sheet_error` (lỗi gửi đơn sang Google Sheets, rỗng khi không có).
- Trạng thái lạ: hiện `status_label` nếu có, không thì giữ nguyên `status`.

## Vé đăng nhập

[Vé đăng nhập một lần](/docs/api/builder-post-login) mở TinoPage Builder đúng tài khoản của dịch vụ, không cần khách đăng nhập lại:

- `target: me` (mặc định) vào trang quản lý; `target: editor` vào trình sửa của `site_id`. Không gửi `site_id`: website cũ nhất của tài khoản (tài khoản chưa có website thì vào trang quản lý).
- Trả 201 `{url, expires_at}`: `url` là https trên máy chủ TinoPage Builder, dùng một lần, hiệu lực 120 giây. Mở ngay trong trình duyệt của chủ dịch vụ; không lưu, không gửi qua kênh khác. Ứng dụng chat tự xem trước link không làm mất vé.
- `login_url` của Tổng quan tài khoản không phải vé: link mở trang đăng nhập TinoPage Builder bằng tài khoản Tino, khách tự đăng nhập.

## Gửi lại an toàn

Mọi lệnh ghi (tạo website, đăng, tạm ngưng, xoá, gắn, kiểm, gỡ tên miền, vé đăng nhập) nhận `idempotency_key` (8–64 ký tự `A-Za-z0-9._:-`); với `DELETE`, gửi ở query. Tạo một khoá cho mỗi lần bấm của khách:

- Mất kết nối giữa chừng thì gửi lại cùng khoá, cùng nội dung: nhận lại kết quả lần trước, lệnh không chạy lần hai. TinoPage Builder nhớ khoá 24 giờ; lần trước lỗi 5xx thì không nhớ, gửi lại sẽ chạy thật.
- Cùng khoá mà nội dung khác, hoặc lần trước còn đang chạy: 409 `IDEMPOTENCY_CONFLICT`. Tải lại dữ liệu trước khi thử với khoá mới.
- Không gửi khoá thì mỗi lần gọi là một lệnh mới.

## Câu trả lời

Thành công: `{"data": …}`, `data` là câu trả lời của TinoPage Builder; danh sách có thêm `meta` (`page`, `per_page`, `total`, `last_page`). HTTP 200, 201 khi gắn tên miền và cấp vé đăng nhập, 202 khi nhận lệnh tạo website. Thời điểm theo RFC 3339; ID website là UUID.

## Lỗi

```json
{ "error": ["LIMIT_EXCEEDED"], "message": "Tài khoản đã dùng hết số website được phép. Nâng cấu hình dịch vụ trên Tino để tạo thêm website.", "details": { "key": "sites" }, "request_id": "…" }
```

Rẽ nhánh theo `error[0]`, không theo `message` (câu tiếng Việt, có thể đổi). Mã viết hoa; `invalid method` (route không có cho dịch vụ này) chỉ có `error`. Khi báo lỗi cho Tino, gửi kèm `request_id`.

| HTTP | Mã | Nghĩa, cách xử lý |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | ID website, tên miền, query, body hoặc `idempotency_key` sai dạng, `details.field` chỉ trường: lỗi lập trình phía ứng dụng |
| 401 | `unauthorized`, `invalid_token`, `token_expired` | token đăng nhập sai hoặc hết hạn: làm mới token |
| 403 | `ACCOUNT_SUSPENDED` | dịch vụ đang tạm ngưng: chặn thao tác ghi, `GET` vẫn dùng được |
| 403 | `ACCOUNT_TERMINATED` | dịch vụ đã chấm dứt: chặn thao tác ghi và vé đăng nhập |
| 404 | `SERVICE_NOT_FOUND`, `invalid method` | không phải dịch vụ TinoPage Builder của tài khoản đang đăng nhập: về danh sách dịch vụ |
| 404 | `SERVICE_NOT_LINKED` | tài khoản TinoPage Builder gắn với dịch vụ khác: tìm lại dịch vụ ([Tìm dịch vụ TinoPage Builder](#tim-dich-vu-tinopage-builder)) |
| 404 | `ACCOUNT_NOT_FOUND` | chưa có tài khoản TinoPage Builder: liên hệ hỗ trợ Tino |
| 404 | `RESOURCE_NOT_FOUND` | website hoặc tên miền không có trong tài khoản (hoặc đã xoá): tải lại danh sách |
| 409 | `LIMIT_EXCEEDED` | hết hạn mức của dịch vụ đã mua, `details.key` chỉ hạn mức: xoá bớt hoặc nâng dịch vụ |
| 409 | `CREATION_IN_PROGRESS` | đang có lượt tạo website: chờ lượt đó xong |
| 409 | `SITE_PAUSED` | website bị hệ thống tạm ngưng vì vượt số website: chưa đăng lại được |
| 409 | `SITE_NOT_PUBLISHED` | đăng website trước rồi mới gắn tên miền riêng |
| 409 | `DOMAIN_TAKEN` | tên miền đang gắn với một website khác |
| 409 | `INVALID_ACTION` | không làm được ở trạng thái hiện tại, `message` nói lý do: tải lại rồi thử lại |
| 409 | `IDEMPOTENCY_CONFLICT` | cùng `idempotency_key` mà nội dung khác, hoặc lần trước còn đang chạy |
| 422 | `VALIDATION_FAILED` | dữ liệu sai: hiện lỗi dưới đúng trường theo `details` (`intake.<ô>`, `hostname`, `kind`, `status`, `target`) |
| 429 | `RATE_LIMITED` | thao tác quá nhanh: thử lại sau ít giây |
| 503 | `AI_UNAVAILABLE` | tính năng tạo website bằng AI tạm dừng: thử lại sau |
| 500, 502, 503 | `INTERNAL_ERROR`, `NETWORK_ERROR`, `INVALID_RESPONSE`, `NOT_CONFIGURED` | kết nối giữa Tino và TinoPage Builder gián đoạn: lỗi phía Tino, không phải lỗi token. Đừng đăng xuất, thử lại sau |
