Send email from a template
Keep one layout for every invoice, welcome or shipping email and fill in the details per message, such as a customer's name or order number 1234. A wording change no longer needs a code change. Pay per use, no subscription.
This task is not available right now, so it cannot be purchased. Nothing is charged for it.
How it works & APImerge your variables
The problem with HTML strings in your codebase
Most teams start by concatenating strings or piping data into an inline template inside the application itself, and it works fine until three things change at once: the marketing team wants a new footer, legal wants a disclaimer added, and a typo in the shipping-confirmation template ships to production because nobody reviewed the string. Storing the template on our side and calling it by reference removes the HTML from your deploy pipeline entirely — a copy change no longer requires a code change.
Why templating email is older than it looks
Mail-merge is not a new idea — it predates the web by decades, going back to form letters generated from a fixed layout and a list of names. The mechanics are the same; the plumbing is what modernized.
Consistency at scale, without a design review each time
Because the layout lives in one place, every invoice, welcome message or shipping update looks the same regardless of which service in your stack triggered it. That consistency matters more than it seems: a customer who gets three visually different emails from the same company in one week starts to wonder if the third one is a phishing attempt.
What you can do with it
Recurring invoice emails
A billing job merges the customer name, amount and due date into a stored invoice template every cycle, without a developer ever touching the layout again.
Welcome sequences
A signup event fires the welcome template with the user's first name and plan details, keeping the brand's onboarding email identical across every signup path.
Multi-language notifications
The same event calls a different stored template per locale, keeping variable names identical while the wording and layout differ by language.
FAQ
Is there a free tier for testing templates?
There's no free tier — free tiers get abused and slow everyone down. No subscription, no tokens, no invented credits, and a failed task is never charged.
Can I use conditional logic inside a template?
The template accepts named placeholders that get merged with the values you send; check the template documentation for the exact placeholder syntax supported.
Can I update a template without redeploying my app?
That's the main advantage: the template lives on our side, so editing its layout or copy never requires a code change or deploy.
How do the blanks in a template work?
You write the message once with named blanks, for example the customer's name and the order number, and send the values with each message. Each blank is replaced by its value before the email is queued, so every customer gets a personal message.
What happens if a send fails?
A failed send is retried automatically three times and is never charged, so it is safe to connect to processes that run unattended. The final outcome comes back to you through a signed notification or a signed link valid for 24 hours.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
One authenticated POST creates the task; the result comes back by webhook or a signed link. This capability is currently available through the API, not as an interactive Web tool.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/notify/email-template \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"template":"Hola {nombre}, tu pedido {id} va en camino.","to":"ada@example.com"}'const res = await fetch("https://api.kit.forhosting.com/notify/email-template", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"template": "Hola {nombre}, tu pedido {id} va en camino.",
"to": "ada@example.com"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/notify/email-template",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"template": "Hola {nombre}, tu pedido {id} va en camino.",
"to": "ada@example.com"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/notify/email-template", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"template":"Hola {nombre}, tu pedido {id} va en camino.","to":"ada@example.com"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"template":"Hola {nombre}, tu pedido {id} va en camino.","to":"ada@example.com"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/notify/email-template", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"template": "Hola {nombre}, tu pedido {id} va en camino.",
"to": "ada@example.com"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "notify.email_template",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
POST /notify/email-template takes the template you've already saved with us and merges in the variables from a single request — a name, an amount, a due date — so the HTML never has to live inside your application code again. It's the endpoint for teams who send the same message shape hundreds of times a day with different details each time.
How the merge actually works
You register a template once, with named placeholders inside it, then call this endpoint with the template identifier and a flat object of values — customer_name, invoice_total, ship_date — and the service fills in the blanks and queues the send. The call returns a task_id immediately; the actual merge and delivery happen a moment later, with the outcome reported back via a signed webhook or a signed link valid for 24 hours.
Why templating email is older than it looks
What's changed is that the 'letter' is now HTML rendered across dozens of inconsistent email clients, and the 'list' is a live API call fired from a checkout or a billing job instead of a spreadsheet.
Fitting it into automated pipelines
A failed task triggers three automatic retries and is never billed, so it's safe to wire directly into flows that run unattended.
Integration use cases
Shipping status updates
A logistics webhook merges tracking number and carrier into the shipping template the moment a package changes status.
Technical questions
How do I register a template?
You store the template with us once, defining named placeholders inside the HTML; after that, every call to this endpoint just references the template and supplies the values.
What does it cost?
$0.002 per request plus $0.0035 per email sent, same as a standard send — templating adds no extra fee.
What happens if I omit a required variable?
The request is validated before the send is queued, so a missing required variable returns a clear error instead of shipping a broken email.
How do I get the result of the send?
The call returns a task_id right away, and the delivery outcome arrives through a signed webhook or a signed link valid for 24 hours.
Is this different from the plain email endpoint?
Yes — the plain endpoint takes a full body in the request, while this one references a stored template and only sends the variables to merge into it.