Templates
Store subject + body server-side and send by referencing a template id, with variable validation and version history.
A template is a subject and body stored on your orboto Mail account instead of inside your application code. This matters for two reasons: you can update the wording of a transactional email (a welcome message, a password reset) without a deploy, and a template can validate its own variables so a caller that forgets one gets a clear error instead of a half-rendered email reaching a customer.
Create a template
curl https://mail.orboto.io/api/v1/templates \
-X POST \
-H "Authorization: Bearer oms_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "welcome",
"subject": "Welcome to Acme, {{name}}!",
"bodyHtml": "<h1>Hi {{name}}</h1><p>Activate your account: <a href=\"{{activationUrl}}\">click here</a>.</p>",
"bodyText": "Hi {{name}}, activate your account: {{activationUrl}}",
"variablesSchema": {
"type": "object",
"required": ["name", "activationUrl"],
"properties": {
"name": { "type": "string" },
"activationUrl": { "type": "string", "format": "uri" }
}
}
}'Placeholders use {{variableName}} syntax in both subject and the
body fields. An unknown placeholder (a typo, or a variable you forgot to
pass) renders as an empty string rather than failing the send - so a
mistake shows up as missing text in the delivered email, not a crash.
Values substituted into bodyHtml are HTML-escaped automatically, so a
variable containing <script> or similar can't inject markup into the
rendered page.
variablesSchema is optional but recommended. It's a small subset of
JSON Schema: required (array of variable names that must be present),
and per-variable type (string/number/integer/boolean), an
optional format (uri or email, checked with a light regex, string
variables only), and an optional enum of allowed values. A template
created without a schema accepts any variables you send it.
The response echoes the stored template with its id, plus
createdAt/updatedAt. Every create and update also appends a version
snapshot server-side, so you (or the account.orboto.io/mail/templates
dashboard) can see the template's edit history.
Send with a template
curl https://mail.orboto.io/api/v1/send \
-X POST \
-H "Authorization: Bearer oms_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme Support <support@yourdomain.example>",
"to": "user@example.com",
"templateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"variables": { "name": "Alice", "activationUrl": "https://app.example.com/activate/abc123" }
}'Pass templateId and variables to POST /v1/send (or as one entry in
a batch send) instead of html/text
- the two are mutually exclusive in the same call.
subjectis optional when a template is used: if you omit it, the template's own subject (with its own{{}}placeholders resolved fromvariables) is used. Ifvariablesfails the template's schema, the send returns400 template_variable_validationwith one message per failed check, and nothing is sent.
Managing templates
| Method + path | Purpose |
|---|---|
GET /v1/templates | List every template on the account, most recently updated first. |
GET /v1/templates/:id | Fetch one template, including its variablesSchema. |
POST /v1/templates | Create (shown above). |
PATCH /v1/templates/:id | Update any subset of name, subject, bodyHtml, bodyText, variablesSchema. |
DELETE /v1/templates/:id | Remove a template. Existing oms_sends history rows that used it are unaffected. |
Templates are private to your account - there is no shared or cross-account template library.
Troubleshooting
| Symptom | Fix |
|---|---|
400 template_variable_validation | Read the message field - it lists every failing variable by name and what's wrong (missing, wrong type, bad format, not in the enum). Fix the caller's variables payload. |
A placeholder like {{usrname}} shows up literally, or renders blank | An unknown placeholder renders as empty, it doesn't error. Check the variable name matches exactly (case-sensitive) between the template body and your variables object. |
404 template_not_found when sending | The templateId doesn't exist on this account, or belongs to a different account/key. Confirm with GET /v1/templates. |
| I need conditionals / loops in a template | The renderer only does flat {{variableName}} substitution - no logic. Build any conditional content into the variables payload before sending (e.g. pre-render an HTML fragment) instead. |