Skip to content

Email & Templates

AnvilBase sends transactional email for auth flows — magic links, email verification, password reset, team invites, and welcome messages. Email delivery is configured per project via SMTP, and every template is customizable.

Configure SMTP

A deployment can set default SMTP in .env (SMTP_HOST, SMTP_PORT, …), but most teams set it per project so each app sends from its own domain.

Local development (Mailpit)

Mailpit is the catch-all SMTP service bundled with the AnvilBase local stack. It captures every outbound email so nothing reaches real inboxes. Browse captured mail at http://localhost:8025.

Terminal window
# One command — no flags needed, Mailpit defaults are pre-set
anvctl projects smtp set <id>
# Confirm the config was saved
anvctl projects smtp show <id>

Production provider

Terminal window
anvctl projects smtp set <id> \
--host smtp.sendgrid.net --port 587 \
--from noreply@myapp.com --from-name "My App" \
--user apikey --encryption starttls
# Password is prompted interactively (not logged / visible in shell history)

Via MCP (AI coding agents)

anvilbase_configure_smtp
project_id: <id>
host: mailpit # or your production relay
port: 1025
from_email: noreply@anvilbase.local
from_name: AnvilBase
encryption: none

Via REST API

Terminal window
curl -X PUT http://localhost:39001/api/v1/projects/<id>/email/smtp \
-H "Authorization: Bearer $ANVILBASE_TOKEN" -H "Content-Type: application/json" \
-d '{
"host": "smtp.example.com",
"port": 587,
"username": "postmaster@example.com",
"password": "•••••",
"from_email": "noreply@example.com",
"from_name": "My App",
"encryption": "starttls"
}'
FieldNotes
host, portSMTP server (1025 = Mailpit, 587 = STARTTLS, 465 = TLS)
username, passwordcredentials (password is write-only — never returned)
from_email, from_namesender identity
encryptionnone (Mailpit/local), starttls (port 587), tls (port 465)

The password is stored AES-256-GCM-encrypted at rest (key derived from the deployment’s CONTROL_PLANE_SECRET) and decrypted only at send time. No read path ever returns it — GET .../smtp omits the field and raw project settings echoes show "[redacted]". Passwords saved by older versions in plaintext keep working and are re-encrypted on the next write, or in one pass via POST /api/v1/admin/encrypt-settings-secrets.

Read it back (without the password):

Terminal window
anvctl projects smtp show <id>
# or
curl http://localhost:39001/api/v1/projects/<id>/email/smtp \
-H "Authorization: Bearer $ANVILBASE_TOKEN"
# → { host, port, username, from_email, from_name, encryption, configured }

Send a test email

Terminal window
curl -X POST http://localhost:39001/api/v1/projects/<id>/email/test \
-H "Authorization: Bearer $ANVILBASE_TOKEN" -H "Content-Type: application/json" \
-d '{"to":"you@example.com","template":"welcome"}'

Returns { success, message }; 400 if SMTP isn’t configured yet. In local dev, the email appears at http://localhost:8025 (Mailpit).

Templates

Five template types, each customizable per project:

template_typeSent when
magic_linka user requests passwordless sign-in
verify_emaila new account needs email confirmation
reset_passworda user requests a password reset
invitea teammate is invited to the project
welcomeautomatically, after a new user signs up (renders {{user_name}})

Template variables

Use these placeholders in subject / body_html / body_text:

VariableValue
{{user_name}}recipient’s name
{{action_url}}the magic link / verify / reset / accept URL
{{project_name}}the project’s name
{{expires_in}}how long the link is valid

Create or update a template

Terminal window
curl -X PUT http://localhost:39001/api/v1/projects/<id>/email/templates/magic_link \
-H "Authorization: Bearer $ANVILBASE_TOKEN" -H "Content-Type: application/json" \
-d '{
"subject": "Your sign-in link for {{project_name}}",
"body_html": "<p>Hi {{user_name}},</p><p>Click to sign in: <a href=\"{{action_url}}\">Sign in</a></p><p>Expires in {{expires_in}}.</p>",
"body_text": "Hi {{user_name}}, sign in: {{action_url}} (expires in {{expires_in}})"
}'

List your templates / see the defaults

Terminal window
# Your custom templates
curl http://localhost:39001/api/v1/projects/<id>/email/templates -H "Authorization: Bearer $ANVILBASE_TOKEN"
# The built-in defaults (subjects + HTML) for all types
curl http://localhost:39001/api/v1/projects/email/templates/defaults -H "Authorization: Bearer $ANVILBASE_TOKEN"

Delete (revert to default)

Terminal window
curl -X DELETE http://localhost:39001/api/v1/projects/<id>/email/templates/magic_link \
-H "Authorization: Bearer $ANVILBASE_TOKEN"

Deleting a custom template falls back to AnvilBase’s built-in default for that type.

In the console

Project → Email tab provides a form for SMTP config and a side-by-side editor (with live preview) for each template. The Send test button uses the project’s SMTP.

Deliverability tips

  • Configure SPF, DKIM, and DMARC for your sending domain.
  • Use a reputable relay (SES, Postmark, Mailgun, Resend) rather than a raw box.
  • Keep from_email on a domain you control and that matches your DKIM signing.
  • Test the full magic-link / reset flows end-to-end after changing SMTP — a silent delivery failure looks identical to a broken auth flow to your users.

Next: Realtime → Overview.