Email Templates
Customize email templates per project.
Customize email templates per project.
Overview
Volcano provides default email templates optimized for deliverability. You can override these with custom templates per project.
Default templates use your project name wherever {{.ProjectName}} appears,
including the message body and footer. Emails for your Volcano account use
Volcano.
Default Templates
Read all built-in templates without authentication:
curl https://api.volcano.dev/email-templates/defaultsThe response contains a data array. Each template has type, subject,
html_body, and text_body. Read one template with
GET /email-templates/defaults/confirmation; its response is the template object
without the data envelope. Built-in templates have no project or template ID.
Confirmation Email
When sent: User signs up with email verification enabled
Default subject: "Confirm your email address"
The built-in template returned by /email-templates/defaults/confirmation uses
"Verify your email address". Signup and resend use the configured confirmation
subject, which defaults to "Confirm your email address".
Contains:
- Confirmation link with token
- Project name
- Security note
Variables:
{{.Email}}- Recipient email{{.ConfirmationURL}}- Link with token{{.ProjectName}}- Your project name
Password Reset Email
When sent: User requests password reset
Default subject: "Reset your password"
Contains:
- Reset link with token
- Expiration warning (1 hour)
- Security notes
- "Didn't request this?" message
Variables:
{{.Email}}- Recipient email{{.ResetURL}}- Link with token{{.ProjectName}}- Your project name
Password Changed Email
When sent: User successfully changes password
Default subject: "Your password was changed"
Contains:
- Confirmation of password change
- Security alert if unauthorized
- Contact info
Variables:
{{.Email}}- Recipient email{{.ProjectName}}- Your project name
Customizing Templates
Custom Subject Lines
Update via configuration:
curl -X PUT https://api.volcano.dev/projects/PROJECT_ID/auth/config \
-H "Authorization: Bearer TOKEN" \
-d '{
"email_confirmation_subject": "Welcome to MyApp!",
"email_password_reset_subject": "Reset your MyApp password",
"email_password_changed_subject": "Security Alert: MyApp password changed"
}'Custom Template Bodies
Every project starts with one row per template type, so overriding a template (HTML + text) is an update:
PUT /projects/{projectId}/email-templates/confirmation
Authorization: Bearer <platform_token>
Content-Type: application/jsonRequest:
{
"subject": "Welcome to MyApp!",
"html_body": "<!DOCTYPE html>...",
"text_body": "Plain text version..."
}Updating or deleting a template that does not exist returns 404 with
{"error":"template not found"}, including updates that change only a body.
Unexpected template failures return 500 with a fixed operation message, such as
{"error":"failed to get email template"}. Responses exclude internal diagnostics.
Responses for custom templates omit subject, html_body, and text_body when
the saved value is empty. Read each field as optional. Built-in template responses
always include all three fields.
If canceling an authentication request (for example, by disconnecting) prevents Volcano from loading the project's saved template, Volcano does not send that email. Signup confirmation and password reset emails can be requested again; welcome and password-change notices cannot. Other lookup failures keep the built-in fallback.
Template types:
confirmation- Email confirmationpassword_reset- Password resetpassword_changed- Password change notification
Template Variables
Available Variables
All templates have access to:
| Variable | Description | Example |
|---|---|---|
{{.Email}} | Recipient email | user@example.com |
{{.ProjectName}} | Project name | My App |
{{.AppName}} | Application name | My App |
{{.ConfirmationURL}} | Confirmation link | https://app.com/confirm?token=abc |
{{.ResetURL}} | Password reset link | https://app.com/reset?token=xyz |
Usage in Templates
<h1>Welcome to {{.ProjectName}}!</h1>
<p>Hi {{.Email}},</p>
<p>Click here to confirm: <a href="{{.ConfirmationURL}}">Confirm Email</a></p>Subject templates can use variables such as {{.Name}}, {{.Email}}, and
{{.ProjectName}}. Welcome emails use the saved welcome subject, or the built-in
welcome subject if it is unset or cannot render. Signup confirmation, password
reset, and password change emails use their auth-configuration subject, or the
send flow's built-in subject when unset. If a configured subject cannot render,
Volcano tries the saved subject, then the email type's built-in subject.
Email-change confirmation uses its built-in subject.
Subject Headers
Keep rendered subject text on one line. Saving a new or changed email-template or auth-configuration subject with a literal interior line break after Go template whitespace trimming returns 400; manifest apply reports 422. Multiline template control flow with {{- and -}} trim markers, outer whitespace, and trailing newlines from YAML block scalars are allowed. Unicode is supported, and long subjects are folded to meet email header line limits without changing the text.
For previews and transactional emails, Volcano trims outer whitespace after rendering a subject that contains a line break. A remaining interior line break, including one introduced by a template variable, returns 400 for a test email or triggers the default transactional subject so confirmation and recovery links still arrive. Message bodies can contain line breaks.
Legacy subjects remain readable and export unchanged. Resubmitting the exact stored subject, including a full configuration save or export reapply, preserves it. Unrelated settings and template bodies remain editable. Changing the subject to another unsafe value is refused; replace it with one safe line using auth configuration or template updates. Delivery still uses the default subject when the rendered legacy value is unsafe.
Deliverability Best Practices
HTML Structure
Do:
- Use simple table-based layouts
- Inline CSS (no external stylesheets)
- Keep under 100KB total size
- Use web-safe fonts
- Include alt text for images
Don't:
- Complex JavaScript
- External resources
- Form elements
- Too many images
Content
Do:
- Clear call-to-action
- Plain text alternative
- Unsubscribe info (for marketing)
- Proper headers
- Simple language
Don't:
- Spam trigger words (FREE!, BUY NOW!)
- All caps
- Excessive punctuation!!!
- Misleading subject lines
Text Alternative
Always include plain text version:
{
"html_body": "<!DOCTYPE html>...",
"text_body": "Plain text version without HTML tags..."
}Why:
- Some clients show text-only
- Improves deliverability
- Accessibility
- Spam filter expects both
Example Custom Template
Minimalist Confirmation
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
<h1>{{.ProjectName}}</h1>
<p>Confirm your email address:</p>
<p>
<a
href="{{.ConfirmationURL}}"
style="display: inline-block; background: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 3px;"
>
Confirm Email
</a>
</p>
<p style="color: #666; font-size: 12px;">If you didn't sign up, ignore this email.</p>
</body>
</html>Text:
{{.ProjectName}}
Confirm your email address by visiting:
{{.ConfirmationURL}}
If you didn't sign up, ignore this email.Branded Template
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body style="background: #f4f4f4; padding: 20px; font-family: 'Helvetica Neue', Arial, sans-serif;">
<div style="max-width: 600px; margin: 0 auto; background: white; border-radius: 8px; overflow: hidden;">
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; text-align: center;">
<h1 style="color: white; margin: 0;">{{.ProjectName}}</h1>
</div>
<div style="padding: 30px;">
<h2>Welcome!</h2>
<p>Thanks for joining {{.ProjectName}}. Click below to confirm your email:</p>
<div style="text-align: center; margin: 30px 0;">
<a
href="{{.ConfirmationURL}}"
style="background: #667eea; color: white; padding: 15px 40px; text-decoration: none; border-radius: 25px; display: inline-block; font-weight: bold;"
>
Confirm Email
</a>
</div>
<p style="color: #666; font-size: 14px;">
Or copy this link: <br />
<span style="word-break: break-all;">{{.ConfirmationURL}}</span>
</p>
</div>
<div style="background: #f9f9f9; padding: 20px; text-align: center; color: #999; font-size: 12px;">
© {{.ProjectName}} - Automated Email
</div>
</div>
</body>
</html>Managing Templates
Create Template
Only needed for a type the project has no row for — a project created with all
four types answers 409 here, so use the update below instead.
curl -X POST https://api.volcano.dev/projects/PROJECT_ID/email-templates \
-H "Authorization: Bearer TOKEN" \
-d '{
"template_type": "confirmation",
"subject": "Welcome!",
"html_body": "...",
"text_body": "..."
}'Update Template
curl -X PUT https://api.volcano.dev/projects/PROJECT_ID/email-templates/confirmation \
-H "Authorization: Bearer TOKEN" \
-d '{
"subject": "Updated subject",
"html_body": "..."
}'Delete Template (Revert to Default)
curl -X DELETE https://api.volcano.dev/projects/PROJECT_ID/email-templates/confirmation \
-H "Authorization: Bearer TOKEN"List Templates
curl https://api.volcano.dev/projects/PROJECT_ID/email-templates \
-H "Authorization: Bearer TOKEN"Testing Templates
Preview Template
Before sending to users, test with your own email:
# 1. Create template
# 2. Sign up with your email
# 3. Check inbox
# 4. Verify rendering in multiple clients:
# - Gmail (web + mobile)
# - Outlook
# - Apple Mail
# - Mobile clientsTemplate Validation
Check:
- All variables render correctly
- Links work
- Renders well on mobile
- Text version is readable
- No broken images
- Unsubscribe info (if applicable)
Security
Template Injection Prevention
Templates use Go's html/template package:
- Automatic HTML escaping
- XSS prevention
- No code execution
Example:
Input: {{.ProjectName}} = "<script>alert(1)</script>"
Output: <script>alert(1)</script>Result: Safe - scripts are escaped
Variable Validation
All template variables are sourced from:
- Database (project name)
- System (confirmation URL, reset URL)
- Never from user input directly
Tokens are:
- Cryptographically random
- Project-scoped
- One-time use
- Time-limited