curl --request POST \
--url https://app.thinnest.ai/api/v1/templates \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "order_ready",
"language": "en",
"category": "utility",
"body": "Hello {{1}}, your Kesar Naturals order {{2}} is packed and on its way.",
"samples": [
"Priya",
"#10432"
],
"footer": "Kesar Naturals, Jaipur",
"buttons": [
{
"kind": "url",
"label": "Track order",
"url": "https://kesarnaturals.in/track/{{1}}",
"sample": "10432"
}
],
"submit": false
}
'{
"name": "order_ready",
"language": "en",
"category": "utility",
"status": "draft",
"agent": "ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e",
"body": "Hello {{1}}, your Kesar Naturals order {{2}} is packed and on its way.",
"variableCount": 2,
"variableLabels": [
{
"position": 1,
"sample": "Priya"
},
{
"position": 2,
"sample": "#10432"
}
],
"buttonVariableCount": 1,
"headerKind": "none",
"createdAt": "2026-10-06T10:00:01.874Z",
"headerText": null,
"footer": "Kesar Naturals, Jaipur",
"buttons": [
{
"kind": "url",
"label": "Track order",
"url": "https://kesarnaturals.in/track/{{1}}"
}
],
"reviewNote": null,
"message": "Saved as a draft."
}{
"error": "`name` is lowercase letters, numbers and underscores only — no spaces or capitals."
}{
"error": "Send a valid API key as `Authorization: Bearer <key>`."
}{
"error": "This API key is read-only: it can read everything but change nothing."
}{
"error": "That agent was not found."
}{
"error": "A template with that name and language already exists."
}{
"error": "Marketing templates need a way out. Add a \"Stop promotions\" quick reply, or put \"Reply STOP to opt out\" in the footer."
}{
"error": "Over 240 requests a minute. Slow down and retry."
}Create Template
Saves a WhatsApp template as a draft, after the console’s own checks of WhatsApp’s rules for the body, header, footer, buttons and an authentication template’s code. Review is a separate, deliberate step — Submit Template, or submit: true here — because once a name is approved it can never be reused for different words; if that submission is refused, the draft is kept and message says why. Limited-time offers, carousels and buttons that open a WhatsApp form are made in the console. Needs a full key.
curl --request POST \
--url https://app.thinnest.ai/api/v1/templates \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "order_ready",
"language": "en",
"category": "utility",
"body": "Hello {{1}}, your Kesar Naturals order {{2}} is packed and on its way.",
"samples": [
"Priya",
"#10432"
],
"footer": "Kesar Naturals, Jaipur",
"buttons": [
{
"kind": "url",
"label": "Track order",
"url": "https://kesarnaturals.in/track/{{1}}",
"sample": "10432"
}
],
"submit": false
}
'{
"name": "order_ready",
"language": "en",
"category": "utility",
"status": "draft",
"agent": "ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e",
"body": "Hello {{1}}, your Kesar Naturals order {{2}} is packed and on its way.",
"variableCount": 2,
"variableLabels": [
{
"position": 1,
"sample": "Priya"
},
{
"position": 2,
"sample": "#10432"
}
],
"buttonVariableCount": 1,
"headerKind": "none",
"createdAt": "2026-10-06T10:00:01.874Z",
"headerText": null,
"footer": "Kesar Naturals, Jaipur",
"buttons": [
{
"kind": "url",
"label": "Track order",
"url": "https://kesarnaturals.in/track/{{1}}"
}
],
"reviewNote": null,
"message": "Saved as a draft."
}{
"error": "`name` is lowercase letters, numbers and underscores only — no spaces or capitals."
}{
"error": "Send a valid API key as `Authorization: Bearer <key>`."
}{
"error": "This API key is read-only: it can read everything but change nothing."
}{
"error": "That agent was not found."
}{
"error": "A template with that name and language already exists."
}{
"error": "Marketing templates need a way out. Add a \"Stop promotions\" quick reply, or put \"Reply STOP to opt out\" in the footer."
}{
"error": "Over 240 requests a minute. Slow down and retry."
}Authorizations
Your API key (ta_live_…) from Settings → API keys, sent as Authorization: Bearer <key>. Keep it on a server: it can message every customer you have. A key is full, build or read-only; a request its level does not allow is refused with 403.
Headers
Developers only: the customer workspace this request acts in — its org_… id from POST /customers. Leave it out to act in your own workspace.
"org_3fKq9TzQ1mN8vB2xR7cLpA"
Body
Lowercase letters, numbers and underscores only. Unique per language in your workspace.
^[a-z0-9_]{1,512}$"order_ready"
utility for something the customer is expecting (an order or appointment update), marketing for promotions, authentication for one-time codes.
utility, marketing, authentication A language WhatsApp templates support, such as en, en_US or hi.
"en"
The message, with blanks numbered {{1}}, {{2}} … in order. A blank cannot be at the very start or end, two cannot sit next to each other, and there must be enough words around them. Required unless category is authentication, where WhatsApp writes the body.
1024"Hello {{1}}, your Kesar Naturals order {{2}} is packed and on its way."
One example value per body blank, in order. WhatsApp's reviewers read the template with these filled in, so every blank needs one.
20["Priya", "#10432"]
The line or media above the body. null means no header.
Show child attributes
Show child attributes
One line of small print, with no blanks. A marketing template needs a way to opt out — "Reply STOP to opt out" here, or a quick-reply button.
60"Reply STOP to opt out"
The template's buttons, in order.
10Show child attributes
Show child attributes
An authentication template's settings. WhatsApp writes the message itself ("123456 is your verification code."), so these are all you choose.
Show child attributes
Show child attributes
The agent the template belongs to (ag_…). Leave it out for the agent on your connected WhatsApp number (or your oldest agent).
"ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e"
true also sends it to WhatsApp for review, as Submit Template does. If that is refused, the draft is kept and message says why.
Response
Saved. The template as Get Template shows it, plus message: saved as a draft, sent for review, or saved as a draft with the reason the submission was refused.
The template, plus a sentence about what just happened.
The template's name — what Send Message and Create Campaign take.
"order_ready"
Its language code.
"en"
WhatsApp's category, which decides its price and its rules.
utility, marketing, authentication draft until submitted, pending while WhatsApp reviews it, then approved or rejected. WhatsApp may later pause an approved template that recipients flag. Only approved can be sent.
draft, pending, approved, rejected, paused The agent the template belongs to (ag_…).
"ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e"
The message, with {{1}}, {{2}} … for the blanks. For an authentication template, the sentence WhatsApp writes around the code.
How many body values a send must carry, in order: the highest {{n}} in the body.
x >= 0The example given for each body blank.
Show child attributes
Show child attributes
How many button values a send must carry: one per link button whose address ends in a blank.
x >= 0The header. image, video or document means a send must pass a media link for it.
none, text, image, video, document When it was created.
A text header's words. null for any other header.
The footer line. Empty when there is none.
The template's buttons, in order.
Show child attributes
Show child attributes
Why WhatsApp refused it, when it did. null otherwise.
What happened: saved as a draft, sent for review, or — with submit: true — saved as a draft with the reason the submission was refused.
"Saved as a draft."