Email templates

Per (account × key × locale × channel) templates. Source format flexible (MJML / React-Email / HTML / plain / markdown). Compilation deferred to the email renderer.

Schema

{
  "email_template": {
    "id": "308...",
    "key": "order.confirmation",
    "locale": "cs",
    "channel": "email",
    "subject": "Tvoje objednávka {{order.name}}",
    "preheader": "Potvrzujeme přijetí objednávky.",
    "source_format": "mjml",
    "body_source": "<mjml>...</mjml>",
    "body_text": null,
    "ctx_schema": {
      "order": { "id": "string", "name": "string", "total_cents": "integer", ... },
      "customer": { "first_name": "string", "email": "string" }
    },
    "category": "order",
    "status": "active",
    "is_default": true,
    "version": 3
  }
}

Upsert a template

curl -X PUT "$API/email_templates.json" \
  -H "Authorization: Bearer $NEVIOS_KEY" -H "Content-Type: application/json" \
  -d '{
    "key": "order.confirmation",
    "locale": "cs",
    "channel": "email",
    "subject": "Děkujeme za objednávku {{order.name}}",
    "preheader": "Brzy ti přijde balíček.",
    "source_format": "mjml",
    "body_source": "<mjml><mj-body><mj-section><mj-column><mj-text>Hello {{customer.first_name}}!</mj-text></mj-column></mj-section></mj-body></mjml>",
    "category": "order",
    "is_default": true
  }'

Upsert keyed on (account, key, locale, channel) — same key across locales lives as separate rows. Optimistic concurrency via expected_version (optional).

Source formats

source_formatBehavior
mjmlCompiled to HTML by the renderer. Mobile-responsive by default. Recommended.
htmlRaw HTML. You're on your own for inboxing / responsive layout.
plainPlain text only. Use for SMS-style minimal emails or fallbacks.
markdownCompiled to HTML. (Currently passthrough — full implementation TODO.)
react_emailReserved slot for React-Email JSX templates (next iteration).

Locale fallback

When sending, the system resolves the best template for the customer's locale via:

1. exact match     (key, locale, channel)
2. base locale     cs-CZ → cs
3. account default 'en'

Want explicit fallback? Set commerce.email.locale_default per account:

curl -X PUT "$API/settings/commerce.email.locale_default.json" \
  -H "Authorization: Bearer $NEVIOS_KEY" -H "Content-Type: application/json" \
  -d '{"scope_type":"account","value":"cs"}'

Standard template keys

Recommended keys (none are auto-required, but if present they fire on these events):

KeyTrigger
order.confirmationOrder created (after checkout.complete)
order.paidPayment status flips to paid
order.shippedFulfillment created
order.deliveredCarrier reports delivery
order.cancelledOrder cancelled
return.requestedCustomer files an RMA
return.completedRefund issued
customer.welcomeCustomer state enabled
voucher.issuedVoucher created for this customer

Variables (Handlebars)

The renderer compiles Handlebars-style variables. Use {{path.to.value}} for substitution. Helpers shipped:

  • {{formatMoney cents 'CZK'}}123.45 CZK
  • {{formatDate isoString}} → locale date
  • {{upper string}} → uppercase
  • {{default value 'fallback'}} → if value is empty
<mj-text>
  Ahoj {{default customer.first_name 'Friend'}},

  Děkujeme za objednávku {{order.name}} ze dne {{formatDate order.created_at}}.
  Celková částka: {{formatMoney order.total_cents 'CZK'}}.
</mj-text>

ctx_schema (AI/MCP introspection)

ctx_schema is a free-form JSON description of expected ctx vars — useful for the dashboard's template editor (autocomplete) and AI agents that compose templates programmatically.

Resolve a template (debug)

curl "$API/email_templates/resolve.json?key=order.confirmation&locale=cs-CZ&channel=email" \
  -H "Authorization: Bearer $NEVIOS_KEY"

Walks the locale fallback chain and returns whatever was found, or 404.

Endpoints

PUT    /admin/2026-01/{handle}/email_templates.json   (upsert)
GET    /admin/2026-01/{handle}/email_templates.json
GET    /admin/2026-01/{handle}/email_templates/resolve.json
DELETE /admin/2026-01/{handle}/email_templates/{id}.json

Next: Notification preferences.