A valid request URL is required to generate request examples{
"template": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"environment_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"key": "<string>",
"variables": [
"<string>"
],
"version": 123,
"channel": "email",
"subject": "<string>",
"body": "<string>",
"url": "<string>",
"category": "marketing",
"archived": true,
"archived_at": "2023-11-07T05:31:56Z",
"system_key": "<string>",
"system": true,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}Create a message template
Requires messaging:manage.
The subject and the body are compiled here, and a template that cannot render is refused rather than stored. {{.Contact.Nickname}} is a 400 template_refused naming the field and listing what is available — not a row that renders <no value> into ten thousand inboxes at three in the morning. A broken template has to fail where somebody is looking.
What a template may say is closed: Contact (.Name, .Email, .ExternalID, and .Attr "key" for anything identify was called with), Customer.Name, Plan.Name, and Entitlements.Has "feature_key". Text, field chains, {{if}}/{{else}} and the standard template functions work; range, with, template and variables are refused, because they rebind . and the check that refuses an unknown field depends on knowing what . is at every point.
category is on the template rather than on whatever sends it, because a category is a statement about what a message says and the template is the only object here that knows what it says. transactional is not one of the values and never will be: a receipt is code, not copy somebody composes, and there is no row that makes a message ignore the unsubscribe list.
An email template needs a subject; an in_app one must not have one — a subject on a channel with no envelope is a field nothing renders.
A valid request URL is required to generate request examples{
"template": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"environment_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"key": "<string>",
"variables": [
"<string>"
],
"version": 123,
"channel": "email",
"subject": "<string>",
"body": "<string>",
"url": "<string>",
"category": "marketing",
"archived": true,
"archived_at": "2023-11-07T05:31:56Z",
"system_key": "<string>",
"system": true,
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}{
"error": {
"code": "forbidden",
"message": "your role does not allow this action"
}
}Authorizations
A staff session token, uk_st_…. Minted by sign-up, sign-in or the two-factor exchange. Only a 401 means it is spent; not_a_member (403) is about the organization named in X-Organization-Id and leaves the token good for the others.
Headers
The organization the caller is acting on — the org_… code that appears in the panel URL. It identifies; the membership JOIN is what authorizes, so a forged code reads nothing: the answer is not_a_member (403), which does not mean the session is over. Absent, the session's default organization answers, or — if that membership was revoked while the session was open — any other one the caller still holds.
Query Parameters
Which environment to act in. A view parameter, valid only on the staff surface — a machine credential never chooses its environment, it is resolved from the key.
live, test Body
How you recognise it. Unique per environment, archived rows included.
200in_app templates are accepted before the widget's notification surface exists — a template written for a channel that has no endpoint yet is a draft, not an error.
email, in_app Which switch the recipient can throw. See the Notifications tag.
marketing, product_news, surveys 100000How a PROGRAM addresses this template — POST /v1/notifications names it here rather than by id. Lowercase letters, digits, underscore and hyphen.
The same key exists once per channel, which is what makes one call reach three doors: fatura_vencendo is a row for email, one for in_app and one for whatsapp, each written for the medium it goes out on.
Omit it for a campaign-only template, addressed by id from a step. A transactional template REQUIRES one — see category.
100"fatura_vencendo"
The placeholders this body may read from the caller's data, declared.
{{.Data.invoice_id}} compiles only when invoice_id is listed here, which is what keeps the save-time check possible with an open value: a typo is refused on the screen of the person who typed it, instead of rendering as an empty half-sentence in ten thousand inboxes. It is also the contract the send validates against.
A list is declared with its columns and is the only thing {{range}} may walk — a loop rebinds the dot, and the columns are what let the check follow it. That is what puts a table in a message without the table being built in your own deploy: the template draws the row, you send the cells.
Names are letters, digits and underscores — a hyphen does not parse inside a template.
A plain value: {{.Data.invoice_id}}.
[
"invoice_id",
{
"name": "membros",
"fields": ["nome", "pontos"]
}
]
Required on email, refused on in_app. Rendered like the body.
500Optional, and in_app only. Where the notifications this template produces lead when somebody acts on one.
A path rooted at / or an absolute http/https URL — nothing else, and never a scheme that executes. Refused on email for the same reason a subject is refused on in_app: an email body here is your own text escaped into our shell, with no call to action for a URL to attach to.
2048Response
The template.
The words this product sends, written once and rendered per person — by a campaign, and since 00165 by POST /v1/notifications too, which addresses it by key and supplies its variables. Per environment, so writing it in test and copying it to live is the obvious workflow rather than a rename.
Show child attributes
Show child attributes