molted templates
Manage email templates
| Command | Description |
|---|---|
molted templates create | Create a new template |
molted templates list | List all templates |
molted templates get | Get a template by ID or slug |
molted templates add-version | Add a new version to a template |
molted templates publish | Publish the latest version of a template |
molted templates delete | Delete a template (by id or slug). Its slug becomes free again. Refused for _default and while an active or paused journey uses it; sent emails keep their history. |
molted templates approve | Approve a pending template version. Human-only (D3, epic #1353): on a tenant that enforces the human gate this returns 403 human_decision_required with a decisionUrl. |
molted templates reject | Reject a pending template version. Human-only (D3, epic #1353): on a tenant that enforces the human gate this returns 403 human_decision_required with a decisionUrl. |
molted templates render | Test-render a template with sample data |
Agent context
Templates define the structure and content of emails. Every send references a template by slug or ID. Templates support {{variable}} placeholders (e.g. {{name}}; plain names only, not Handlebars helpers or dot paths) that are filled from the --payload at send time.
The _default template
- Auto-created with every tenant — always available
- Accepts: { "subject": "...", "html": "...", "text": "..." } as payload
- Use it for one-off emails; create named templates for recurring sends
Create and publish a template in one step. Variables used in --subject/--body must be declared with --variables, and a marketing template (the default when --type is omitted) needs an unsubscribe link or it fails to lint:
molted templates create --name "Welcome" --slug welcome \
--subject "Welcome, {{name}}!" \
--body "<h1>Hello {{name}}</h1><p><a href='{{unsubscribe_url}}'>Unsubscribe</a> · {{physical_address}}</p>" \
--text "Hello {{name}}" \
--variables '[{"name": "name", "type": "string", "required": true}]' \
--auto-publish
# → { "template": {...}, "version": { ..., "lintPassed": true }, ... }Then send with it
molted send --to alice@example.com --template welcome \
--dedupe-key "welcome-alice-1" --payload '{"name": "Alice"}'Template versioning (without --auto-publish)
molted templates create --name "Promo" --slug promo
molted templates add-version <id> --subject "{{offer}}" \
--body "<p>{{offer}}</p><p><a href='{{unsubscribe_url}}'>Unsubscribe</a> · {{physical_address}}</p>" \
--variables '[{"name": "offer", "type": "string", "required": true}]'
molted templates publish <id>Approval workflow (for regulated sends)
molted templates create --name "Legal" --slug legal-notice --type transactional --approval-required
molted templates add-version <id> --subject "Notice" --body "<p>Your account was updated.</p>"
molted templates publish <id>
# → returns an approvalId (version is pending, not live yet)
molted templates approve <approvalId> --reviewed-by "alice"
# → version is now live and can be sent
# Approving is a human decision: once the account's human gate enforces, an
# agent key gets 403 human_decision_required (a person approves in the portal).Test rendering before sending
molted templates render <id> --payload '{"name": "Alice", "offer": "20% off"}'
# → { "subject": "...", "html": "...", "text": "...", "errors": [], "warnings": [] }
# Renders the published version (422 if the template has none published yet).Delete a template you no longer need (e.g. a test one)
molted templates delete <id-or-slug>
# → { "deleted": true, "id": "...", "slug": "...", "rejectedApprovals": 0 }The slug is free again right away, so you can recreate a template with it. Refused for _default, and while an active or paused journey has a step that uses the template (the error names those journeys: archive or delete them, or point their steps at another template, first). Versions waiting for approval are rejected. Emails already sent with it keep their history.
Template types
- "transactional" - receipts, confirmations, password resets; exempt from the unsubscribe-link and physical-address lint rules
- "marketing" - newsletters, promos (subject to the consent gate); needs an unsubscribe link to pass lint ({{unsubscribe_url}} is filled at send time). A marketing send is blocked until the account has a physical address on file, which Molted appends as a footer; a {{physical_address}} placeholder only satisfies the lint warning and renders empty.
--type defaults to "marketing" when omitted -- pass --type transactional if that's not what you want. Omitting --type prints a stderr note as a reminder.
Related: molted send, molted mailboxes
Commands
molted templates create
Create a new template
molted templates create [options]| Option | Description | Default |
|---|---|---|
--name <name> | Template name (required) (required) | |
--slug <slug> | Template slug (required) (required) | |
--type <type> | Template type (defaults to marketing if omitted) (one of: marketing, transactional) | |
--approval-required | Require approval before publishing | |
--subject <subject> | Subject template | |
--body <html> | HTML body template | |
--text <text> | Plain text body template | |
--variables <json> | Template variables (JSON array) | |
--auto-publish | Auto-publish the initial version |
molted templates list
List all templates
molted templates listmolted templates get
Get a template by ID or slug
molted templates get <id>| Argument | Description | Required |
|---|---|---|
<id> | yes |
molted templates add-version
Add a new version to a template
molted templates add-version [options] <id>| Argument | Description | Required |
|---|---|---|
<id> | yes |
| Option | Description | Default |
|---|---|---|
--subject <subject> | Subject template (required) (required) | |
--body <html> | HTML body template (required) (required) | |
--text <text> | Plain text body template | |
--variables <json> | Template variables (JSON array) |
molted templates publish
Publish the latest version of a template
molted templates publish [options] <id>| Argument | Description | Required |
|---|---|---|
<id> | yes |
| Option | Description | Default |
|---|---|---|
--requested-by <name> | Who requested the publish | cli |
molted templates delete
Delete a template (by id or slug). Its slug becomes free again. Refused for _default and while an active or paused journey uses it; sent emails keep their history.
molted templates delete <id>| Argument | Description | Required |
|---|---|---|
<id> | yes |
molted templates approve
Approve a pending template version. Human-only (D3, epic #1353): on a tenant that enforces the human gate this returns 403 human_decision_required with a decisionUrl.
molted templates approve [options] <approvalId>| Argument | Description | Required |
|---|---|---|
<approvalId> | yes |
| Option | Description | Default |
|---|---|---|
--reviewed-by <name> | Who reviewed the template | cli |
--comment <comment> | Review comment |
molted templates reject
Reject a pending template version. Human-only (D3, epic #1353): on a tenant that enforces the human gate this returns 403 human_decision_required with a decisionUrl.
molted templates reject [options] <approvalId>| Argument | Description | Required |
|---|---|---|
<approvalId> | yes |
| Option | Description | Default |
|---|---|---|
--reviewed-by <name> | Who reviewed the template | cli |
--reason <reason> | Rejection reason |
molted templates render
Test-render a template with sample data
molted templates render [options] <id>| Argument | Description | Required |
|---|---|---|
<id> | yes |
| Option | Description | Default |
|---|---|---|
--payload <json> | Template payload as JSON object (required) (required) |
Note: --payload is required. Pass a JSON object matching the template's variables.
Example
molted templates render <id> --payload '{"name": "Alice", "offer": "20% off"}'
# → returns rendered HTML and text with variables substitutedUse this to verify rendering before sending. The _default template expects:
{ "subject": "...", "html": "...", "text": "..." }See CLI overview for global options, credentials and output format.