Skip to content

Templates

Templates are reusable notification definitions with variable substitution.

They let you keep message content centralized while producers focus on sending structured event payloads.

What Templates Are

A template defines message content for one channel.

You can reference a template by template_id or template_name when creating events.

Beaco renders the template using values from event payload.

Benefits:

  • consistency across notifications
  • easier content updates
  • less duplication in producer services

Template Fields

Each template has a core schema:

FieldTypeRequiredNotes
namestringyesHuman-readable identifier
channelenumyesemail, sms, or webhook
subjectstringemail onlyPlain-text subject; values are never HTML-escaped
bodystringyesHTML for email; injected values are HTML-escaped
text_bodystringnoPlain-text alternative; derived from HTML when absent
from_localstringnoEmail sender name before the @; see Sender and Reply-To
from_namestringnoEmail sender display name
reply_tostringnoEmail address replies are sent to
variablesarray<string>noAuto-detected; must exactly match when supplied
on_missing_variableenumnoerror (default) or legacy-compatible blank

Sender and Reply-To

Email templates can choose who the email comes from and where replies go. All three fields are optional; when none is set, the email is sent from the server's default sender, exactly as before.

FieldRules
from_localThe part before the @: lowercase letters, digits, ., _, +, - (max 64)
from_namePlain-text display name, no control characters or newlines (max 100)
reply_toAny valid email address. It needs no DNS setup

The domain always comes from the server's verified sending address, so a template can never send from a different domain. A template with from_local billing and from_name Acme Billing sends as Acme Billing <billing@your-verified-domain>. Values are fixed text, not {{ variables }}.

JSON
{
  "name": "order-confirmed",
  "channel": "email",
  "subject": "Order {{ order_number }} confirmed",
  "body": "<h1>Thanks, {{ customer_name }}</h1>",
  "from_local": "orders",
  "from_name": "Winwell Orders",
  "reply_to": "[email protected]"
}

To remove a sender field later, update the template with that field set to null. Emails sent inline (without a template) set the same fields on inline; see API reference.

Jinja2 Variable Syntax

Beaco templates use Jinja2-style placeholders:

Text
{{ variable_name }}

You can use variables in:

  • subject (email templates)
  • body (all channels)
  • text_body (email templates)

Example:

Text
Welcome, {{ user_name }}!

Create a Template

Endpoint:

Text
POST /templates

Example request:

Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/templates \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d '{
    "name": "welcome-email",
    "channel": "email",
    "subject": "Welcome, {{ user_name }}!",
    "body": "<h1>Hi {{ user_name }}</h1><p>Welcome to {{ app_name }}.</p>",
    "text_body": "Hi {{ user_name }} — welcome to {{ app_name }}.",
    "on_missing_variable": "error"
  }'

Example response:

JSON
{
  "id": "tmpl_01j4zb7h0ws8f5m6a2x1v4q7pk",
  "name": "welcome-email",
  "channel": "email",
  "variables": ["app_name", "user_name"],
  "detected_variables": ["app_name", "user_name"],
  "created_at": "2026-01-12T11:24:19Z"
}

If variables differs from the variables detected in the subject or bodies, Beaco returns 422. New templates default to on_missing_variable: "error"; a missing payload value permanently fails that notification without retrying or sending an email with a blank hole.

Sync a Template on Every Deploy

PUT /templates/by-name/{name}?channel=email creates the template once and updates that same template on later deploys.

Bash
curl -X PUT 'https://beaco.michaelogunjimi.com/api/v1/templates/by-name/order-confirmed?channel=email' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: YOUR_PROJECT_KEY' \
  -d '{
    "subject": "Order {{ order_number }} confirmed",
    "body": "<main style=\"font-family:sans-serif;color:#172033\"><h1>Thanks, {{ customer_name }}</h1><p>Order <strong>{{ order_number }}</strong> is confirmed.</p></main>",
    "text_body": "Thanks, {{ customer_name }}. Order {{ order_number }} is confirmed."
  }'

Import Existing HTML

POST /templates/import turns sample values in HTML text nodes into variables and returns the created template plus a preview. It rejects samples found in attributes or anywhere other than one unambiguous text occurrence.

JSON
{
  "name": "branded-order",
  "subject": "Your order is confirmed",
  "html": "<main><h1>Thanks, Chidi</h1><p>Order BEA-1042 is confirmed.</p></main>",
  "variables": {
    "customer_name": "Chidi",
    "order_number": "BEA-1042"
  }
}

Use a Template in Event Creation

When creating an event:

  1. set template_id
  2. provide payload keys matching required variables

Example:

Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/events \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_PROJECT_KEY" \
  -d '{
    "event_type": "user.welcome",
    "template_id": "tmpl_01j4zb7h0ws8f5m6a2x1v4q7pk",
    "recipients": [{
      "channels": ["email"],
      "email": "[email protected]"
    }],
    "payload": {
      "user_name": "Alice",
      "app_name": "Beaco Cloud"
    }
  }'

Preview a Template

Use preview to test rendering without sending notifications.

Endpoint:

Text
POST /templates/{id}/preview

Example:

Bash
curl -X POST https://beaco.michaelogunjimi.com/api/v1/templates/tmpl_01j4zb7h0ws8f5m6a2x1v4q7pk/preview \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d '{
    "variables": {
      "user_name": "Alice",
      "app_name": "Beaco Cloud"
    }
  }'

Example preview response:

JSON
{
  "subject": "Welcome, Alice!",
  "html": "<h1>Hi Alice</h1><p>Welcome to Beaco Cloud.</p>",
  "text": "Hi Alice — welcome to Beaco Cloud.",
  "variables_used": ["app_name", "user_name"],
  "missing_variables": [],
  "body": "<h1>Hi Alice</h1><p>Welcome to Beaco Cloud.</p>"
}

Use preview during CI checks for template integrity before deployment.

Template Ownership Model

Every template is associated with the API key that created it.

Ownership implications:

  • keys with templates:read can inspect templates available to that integration
  • keys with templates:write can create and manage their own templates
  • internal system defaults may be made available by the platform but are not managed through a master key
  • authorized platform administrators use the separate admin control plane

This model supports multi-tenant safety and predictable content boundaries.

Channel-Specific Guidance

Email Templates

Use:

  • descriptive subjects
  • concise intro paragraphs
  • explicit CTAs

Template example:

Text
Subject: Invoice {{ invoice_id }} paid
Body: Hi {{ user_name }}, your payment of {{ amount }} was received.

SMS Templates

Prefer short, direct bodies to stay within segment limits.

Template example:

Text
{{ app_name }}: Your OTP is {{ otp_code }}. Expires in {{ expires_in_minutes }} minutes.

Webhook Templates

Webhook channel body usually contains JSON-oriented data fields.

Template example:

Text
{"type":"{{ event_type }}","customer_id":"{{ customer_id }}","status":"{{ status }}"}

Validation and Common Errors

ErrorTypical CauseFix
Missing variable at renderPayload key not providedAdd expected key to event payload
Invalid channel-template pairingTemplate channel differs from recipient channelUse matching template/channel
Unauthorized template updateWrong API key scopeUse a project key with template write access
Invalid Jinja syntaxUnclosed braces or malformed expressionsValidate template text and preview

Versioning Strategy

Recommended pattern:

  1. create new template version with revised content
  2. migrate producer usage gradually
  3. deprecate old templates after validation

This avoids abrupt behavior changes for active event producers.

Best Practices

  1. Keep variables explicit and stable (user_name, not u).
  2. Use template names tied to domain actions (welcome-email, invoice-paid).
  3. Preview templates before production rollout.
  4. Store long-form content in templates, not in producer payload.
  5. Avoid embedding business logic in template expressions.

Minimal Python Example

Python
import requests

BASE_URL = "https://beaco.michaelogunjimi.com/api/v1"
API_KEY = "YOUR_PROJECT_KEY"

payload = {
    "event_type": "user.welcome",
    "template_id": "tmpl_01j4zb7h0ws8f5m6a2x1v4q7pk",
    "recipients": [{"channels": ["email"], "email": "[email protected]"}],
    "payload": {"user_name": "Alice", "app_name": "Beaco"}
}

resp = requests.post(
    f"{BASE_URL}/events",
    json=payload,
    headers={"X-API-Key": API_KEY}
)

print(resp.status_code, resp.json())