Create ZBS Template
This API allows you to create a Zalo Business Solution (ZBS) Template with full display content and dynamic parameters.
Usage flow:
- Create the template with this API — the template is saved with status
DRAFTand the response returns its_id. - Publish the template with the Publish ZBS Template API to submit it to Zalo for review (
PENDING_REVIEW). - Once Zalo approves it (
ENABLE), the template can be used to send messages.
Starting from the latest version, the display content of a template is declared through the contentBlocks array (paragraph, table, voucher, rating) instead of the previous paragraphs and tables fields.
Endpoint
POST https://staging-manage.api.miniai.vn/api/external/zns-template/create
POST https://manage-api.miniap.vn/api/external/zns-template/create
Authentication
Every request must include your API key in the header:
| Header | Value | Required |
|---|---|---|
x-api-key | miniai-partner <YOUR_API_KEY> | Bắt buộc |
Content-Type | application/json | Bắt buộc |
The shop is identified from the API key itself, so the system assigns shopId to the template for you. You do not need to (and cannot) pass shopId in the request body.
Parameters (Request Body)
General information
| Field | Type | Required | Description |
|---|---|---|---|
oaId | string | Bắt buộc | ID of the Zalo OA used to send messages. Required before the template can be published. |
name | string | Bắt buộc | Template name (for internal management). |
description | string | Tùy chọn | Short description of the template's purpose. |
title | string | Bắt buộc | Title shown in the message. Dynamic parameters such as <customer_fullname> can be inserted. |
tag | "1" | "2" | "3" | Bắt buộc | Template content level — see Template level. |
templateType | custom | voucher | rating | Bắt buộc | Template type — see Template type. |
imageType | logo | image | Bắt buộc | Header image style: use a logo or a banner image. |
logoLight | string | Tùy chọn | Logo URL for light background. Required when imageType = logo (validated on publish). |
logoDark | string | Tùy chọn | Logo URL for dark background. Required when imageType = logo. |
images | string[] | Tùy chọn | List of image URLs. At least one image is required when imageType = image. |
contentBlocks | object[] | Bắt buộc | Message body content — see contentBlocks. |
buttons | object[] | Bắt buộc | List of buttons, maximum 3 — see buttons. |
params | object[] | Bắt buộc | Dynamic parameter declarations — see params. |
voucher | object | Có điều kiện | Required when templateType = voucher — see Voucher template. |
rating | object | Có điều kiện | Required when templateType = rating — see Rating template. |
isCustomParams | boolean | Bắt buộc | true if you declare the parameters in params yourself; false to use the system's default parameter set. |
note | string | Tùy chọn | Note sent to Zalo's review team (explaining the message's use case). |
Templates are always created with status DRAFT, even if you include status in the request. Status changes happen through the Publish ZBS Template API.
Template level (tag)
Zalo classifies messages by content level; the level affects review policy and message pricing.
tag | Level | Suitable content |
|---|---|---|
"1" | Transaction | Account verification, OTP, order/transaction confirmation, balance changes… |
"2" | Customer care | Loyalty point updates, policy updates, opinion surveys, birthday greetings… |
"3" | Post-sale | New product/service announcements, discount codes and promotions, renewal invitations… |
Template type (templateType)
| Value | Name | Description |
|---|---|---|
custom | Custom message | Free-form message: transaction notices, OTP, reminders… |
voucher | Voucher template | Send a discount code / voucher with conditions and expiry. |
rating | Rating template | Collect customer feedback after using a product/service. |
Message content (contentBlocks)
contentBlocks is an array of content blocks, rendered in the exact order you declare them.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Bắt buộc | Unique identifier of the block (a UUID you generate). |
type | paragraph | table | voucher | rating | Bắt buộc | Content block type. |
value | string | Có điều kiện | Text content — used by paragraph blocks. Parameters such as <order_code> can be inserted. |
rows | object[] | Có điều kiện | Table rows — used by table blocks. |
voucher | object | Có điều kiện | Voucher information — used by voucher blocks. |
rating | object | Có điều kiện | Rating information — used by rating blocks. |
Table rows (rows)
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Bắt buộc | Label shown on the left (e.g. Order code). |
key | string | Bắt buộc | Value shown on the right; use <parameter_name> to insert a dynamic parameter. |
rowType | number | Tùy chọn | Row color effect, defaults to 0. |
rowType values:
rowType | Meaning |
|---|---|
0 | No effect |
1 | Success (green) |
2 | Update (blue) |
3 | Warning (yellow) |
4 | Error (red) |
5 | Basic |
Dynamic parameters (params)
Every parameter used in title, a paragraph, or rows must be declared in params.
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Bắt buộc | Parameter name, without the <> brackets (e.g. order_code). |
label | string | Bắt buộc | Display name of the parameter in the editor. |
techSetting | string | Bắt buộc | Zalo's technical data type — determines review rules and maximum length. |
maxLength | number | Bắt buộc | Maximum length of the submitted value, matching the techSetting. |
sampleValue | string | Bắt buộc | Sample value used by Zalo for preview and review. |
techSetting values and their maxLength:
techSetting | Data type | maxLength |
|---|---|---|
"1" | Customer name | 30 |
"2" | Phone number | 15 |
"3" | Address | 200 |
"4" | Code (order code, customer code…) | 30 |
"5" | Custom label | 30 |
"6" | Transaction status | 30 |
"7" | Contact information | 50 |
"8" | Gender / Title | 5 |
"9" | Product name / Brand | 200 |
"10" | Quantity / Amount | 20 |
"11" | Time | 20 |
"12" | OTP | 10 |
"13" | URL | 200 |
"14" | Currency (VND) | 12 |
"15" | Bank transfer note | 90 |
A parameter without techSetting causes the publish template request to fail with 400. Choosing the wrong technical type (e.g. using Custom label for an amount) is a common reason Zalo rejects a template.
Buttons (buttons)
A maximum of 3 buttons per template.
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Bắt buộc | Text shown on the button. |
type | string | Bắt buộc | Button action type — see the table below. |
value | string | Bắt buộc | Destination value of the action (URL, phone number, Mini App ID…). Can be an empty string for actions that need no value. |
type | Action |
|---|---|
"1" | Go to the business page |
"2" | Call (value is a phone number) |
"3" | Go to the OA information page |
"4" | Go to the business's Zalo Mini App |
"5" | Go to the business's app page |
"6" | Go to the product distribution page |
"7" | Go to another website / Zalo Mini App |
"8" | Go to another app |
"9" | Go to a business article |
"10" | Go to an app-download call-to-action landing page |
Voucher template
When templateType = voucher, the top-level voucher field is required:
| Field | Type | Description |
|---|---|---|
code | string | Voucher code (usually a dynamic parameter, e.g. <voucher_code>). |
title | string | Program / offer name. |
condition | string | Applicable conditions. |
startDate | string | Start date of validity. |
expiryDate | string | Expiry date. |
The top-level voucher (and rating) field is used for input validation only — it is not stored and is not returned in the response. What actually renders in the message is the voucher / rating block inside contentBlocks, so declare matching values in both places and read the values back from contentBlocks.
Rating template
When templateType = rating, the top-level rating field is required and contains an items array (at least one element):
| Field | Type | Description |
|---|---|---|
star | number | Star count for that answer scenario (1–5). |
title | string | Title shown for that star level. |
question | string | Follow-up question for the customer. |
answers | string[] | List of suggested answers. |
thanks | string | Thank-you message after the customer rates. |
description | string | Additional description. |
Example Request (cURL)
- cURL
curl -X POST 'https://manage-api.miniap.vn/api/external/zns-template/create' \
-H 'x-api-key: miniai-partner <YOUR_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"oaId": "2288954399991473926",
"name": "Order confirmation",
"description": "Order confirmation notice for the customer",
"title": "Hello <customer_fullname>,",
"tag": "1",
"templateType": "custom",
"imageType": "logo",
"logoLight": "https://cdn.miniap.vn/zns/logo-light.png",
"logoDark": "https://cdn.miniap.vn/zns/logo-dark.png",
"isCustomParams": true,
"contentBlocks": [
{
"id": "10c8db12-7f66-4b53-a79c-22b5a01dac49",
"type": "paragraph",
"value": "Thank you for your order. Your order has been confirmed."
},
{
"id": "6f2b0a54-1f5a-4f0f-9a52-0f5b2b6c7d10",
"type": "table",
"rows": [
{ "label": "Order code", "key": "<order_code>", "rowType": 0 },
{ "label": "Status", "key": "<order_status>", "rowType": 1 }
]
}
],
"buttons": [
{ "label": "View order", "value": "https://manage.miniai.vn/orders", "type": "1" }
],
"params": [
{ "key": "customer_fullname", "label": "Customer fullname", "techSetting": "1", "maxLength": 30, "sampleValue": "Nguyễn Văn A" },
{ "key": "order_code", "label": "Order code", "techSetting": "4", "maxLength": 30, "sampleValue": "DH123456" },
{ "key": "order_status", "label": "Order status", "techSetting": "6", "maxLength": 30, "sampleValue": "Confirmed" }
]
}'
The shopId is resolved from your API key on the server, so it is omitted here. The full field-by-field body is described in the examples below.
Request Body examples
- Custom message
- Voucher template
- Rating template
{
"oaId": "2288954399991473926",
"name": "Order confirmation",
"description": "Order confirmation notice for the customer",
"title": "Hello <customer_fullname>,",
"tag": "1",
"templateType": "custom",
"imageType": "logo",
"logoLight": "https://cdn.miniap.vn/zns/logo-light.png",
"logoDark": "https://cdn.miniap.vn/zns/logo-dark.png",
"images": [],
"isCustomParams": true,
"contentBlocks": [
{
"id": "10c8db12-7f66-4b53-a79c-22b5a01dac49",
"type": "paragraph",
"value": "Thank you for shopping with us. Your order has been confirmed."
},
{
"id": "6f2b0a54-1f5a-4f0f-9a52-0f5b2b6c7d10",
"type": "table",
"rows": [
{ "label": "Order code", "key": "<order_code>", "rowType": 0 },
{ "label": "Status", "key": "<order_status>", "rowType": 1 },
{ "label": "Total payment", "key": "<order_price>", "rowType": 0 }
]
}
],
"buttons": [
{
"label": "View order",
"value": "https://manage.miniai.vn/orders",
"type": "1"
}
],
"params": [
{
"key": "customer_fullname",
"label": "Customer fullname",
"techSetting": "1",
"maxLength": 30,
"sampleValue": "Nguyễn Văn A"
},
{
"key": "order_code",
"label": "Order code",
"techSetting": "4",
"maxLength": 30,
"sampleValue": "DH123456"
},
{
"key": "order_status",
"label": "Order status",
"techSetting": "6",
"maxLength": 30,
"sampleValue": "Confirmed"
},
{
"key": "order_price",
"label": "Order price",
"techSetting": "14",
"maxLength": 12,
"sampleValue": "1500000"
}
],
"note": "Message sent when an order is confirmed"
}
{
"oaId": "2288954399991473926",
"name": "Birthday voucher gift",
"title": "Happy birthday <customer_fullname>!",
"tag": "3",
"templateType": "voucher",
"imageType": "image",
"images": ["https://cdn.miniap.vn/zns/birthday-banner.png"],
"isCustomParams": true,
"contentBlocks": [
{
"id": "1c2f9a11-2b34-4d55-9f7a-88b0a1cd0f21",
"type": "paragraph",
"value": "We're sending you a discount code to celebrate your birthday."
},
{
"id": "35b8de07-4c11-4f2a-88de-9a1c2e3b4d55",
"type": "voucher",
"voucher": {
"code": "<voucher_code>",
"title": "20% off the entire order",
"condition": "Applies to orders from 300,000 VND",
"startDate": "2025-10-01",
"expiryDate": "2025-10-31"
}
}
],
"voucher": {
"code": "<voucher_code>",
"title": "20% off the entire order",
"condition": "Applies to orders from 300,000 VND",
"startDate": "2025-10-01",
"expiryDate": "2025-10-31"
},
"buttons": [
{ "label": "Mua ngay", "value": "https://manage.miniai.vn/shop", "type": "7" }
],
"params": [
{
"key": "customer_fullname",
"label": "Customer fullname",
"techSetting": "1",
"maxLength": 30,
"sampleValue": "Nguyễn Văn A"
},
{
"key": "voucher_code",
"label": "Voucher code",
"techSetting": "4",
"maxLength": 30,
"sampleValue": "BDAY20"
}
],
"note": "Birthday voucher gift message"
}
{
"oaId": "2288954399991473926",
"name": "Post-purchase survey",
"title": "Hello <customer_fullname>,",
"tag": "2",
"templateType": "rating",
"imageType": "logo",
"logoLight": "https://cdn.miniap.vn/zns/logo-light.png",
"logoDark": "https://cdn.miniap.vn/zns/logo-dark.png",
"isCustomParams": true,
"contentBlocks": [
{
"id": "9a0f31bd-7d22-4a6d-8b39-1e0c5f2a7c44",
"type": "paragraph",
"value": "Were you happy with your recent shopping experience?"
},
{
"id": "b71c4d92-5e88-4b1c-9f30-7c2a6e0d8b13",
"type": "rating",
"rating": {
"items": [
{
"star": 5,
"title": "Very satisfied",
"question": "What did you like most?",
"answers": ["Great product", "Fast delivery"],
"thanks": "Thank you for your feedback!",
"description": ""
}
]
}
}
],
"rating": {
"items": [
{
"star": 5,
"title": "Very satisfied",
"question": "What did you like most?",
"answers": ["Great product", "Fast delivery"],
"thanks": "Thank you for your feedback!",
"description": ""
}
]
},
"buttons": [],
"params": [
{
"key": "customer_fullname",
"label": "Customer fullname",
"techSetting": "1",
"maxLength": 30,
"sampleValue": "Nguyễn Văn A"
}
],
"note": "Post-purchase survey message"
}
Response
- Response
- 400 - Validation
- 401 - Authentication
- 429 - Rate limited
{
"template": {
"name": "Order confirmation",
"description": "Order confirmation notice for the customer",
"oaId": "2288954399991473926",
"tag": "1",
"templateType": "custom",
"imageType": "logo",
"title": "Hello <customer_fullname>,",
"contentBlocks": [
{
"id": "10c8db12-7f66-4b53-a79c-22b5a01dac49",
"type": "paragraph",
"value": "Thank you for shopping with us. Your order has been confirmed."
},
{
"id": "6f2b0a54-1f5a-4f0f-9a52-0f5b2b6c7d10",
"type": "table",
"rows": [
{ "label": "Order code", "key": "<order_code>", "rowType": 0 },
{ "label": "Status", "key": "<order_status>", "rowType": 1 },
{ "label": "Total payment", "key": "<order_price>", "rowType": 0 }
]
}
],
"buttons": [
{
"label": "View order",
"value": "https://manage.miniai.vn/orders",
"type": "1"
}
],
"params": [
{
"key": "customer_fullname",
"label": "Customer fullname",
"techSetting": "1",
"maxLength": 30,
"sampleValue": "Nguyễn Văn A"
},
{
"key": "order_code",
"label": "Order code",
"techSetting": "4",
"maxLength": 30,
"sampleValue": "DH123456"
},
{
"key": "order_status",
"label": "Order status",
"techSetting": "6",
"maxLength": 30,
"sampleValue": "Confirmed"
},
{
"key": "order_price",
"label": "Order price",
"techSetting": "14",
"maxLength": 12,
"sampleValue": "1500000"
}
],
"logoDark": "https://cdn.miniap.vn/zns/logo-dark.png",
"logoLight": "https://cdn.miniap.vn/zns/logo-light.png",
"images": [],
"note": "Message sent when an order is confirmed",
"shopId": "64204a17a5a97a86f12e1f0a",
"status": "DRAFT",
"isSync": false,
"isCustomParams": true,
"_id": "68c8ee675f8f83fc813e7830",
"createdAt": "2025-09-16T04:58:15.668Z",
"updatedAt": "2025-09-16T04:58:15.668Z",
"id": "68c8ee675f8f83fc813e7830"
}
}
{
"error": "Bad request",
"message": "\"oaId\" is required, \"params[0].techSetting\" is required"
}
The response is plain text (not JSON):
Unauthorized
{
"error": "Too many requests, limit to 100 requests per minute"
}
Common errors
| Code | Cause |
|---|---|
400 | Missing required field, wrong data type, invalid techSetting, or more than 3 buttons |
401 | Missing x-api-key header, wrong miniai-partner prefix, or invalid API key |
429 | Request limit exceeded (see the Rate limit section below) |
500 | System error |
Rate limit
Each shop may call at most 100 requests per minute across the entire External API.
Exceeding the limit returns a 429.
Use the _id from the response as the template_id when calling the Publish ZBS Template API, then look it up again with Get template detail.