Skip to main content

Migrating Auth Email Templates

If you're moving to Zyphr from another auth provider, you most likely already have branded transactional emails for password reset, magic link, and email verification. You shouldn't have to rebuild those from scratch.

This guide shows how to import your existing templates via the Zyphr API in a single round-trip per application.

What you need

  • A Zyphr application public key and secret key (za_live_pub_... and za_live_sec_...).
  • Your existing email HTML — it doesn't matter what tool generated it. If it was MJML, even better — MJML is supported natively.
  • The variable names your old provider used (you'll remap them to Zyphr's variable contract).

Variable mapping

Zyphr's auth emails take a fixed set of variables per email type. Adjust your imported templates to reference them.

From Auth0

Auth0 variableZyphr variable
{{ application.name }}{{application_name}}
{{ url }} (in password change email){{reset_url}}
{{ url }} (in magic link email){{magic_link_url}}
{{ url }} (in verification email){{verify_url}}
{{ user.email }}{{user_email}}
{{ user.user_metadata.name }}{{user_name}}

From Firebase Authentication

Firebase variableZyphr variable
%APP_NAME%{{application_name}}
%LINK% (in password reset email){{reset_url}}
%LINK% (in email verification){{verify_url}}
%EMAIL%{{user_email}}
%DISPLAY_NAME%{{user_name}}

From Clerk

Clerk variableZyphr variable
{{app.name}}{{application_name}}
{{action_link}} (magic link){{magic_link_url}}
{{action_link}} (password reset){{reset_url}}
{{otp_code}}(use a magic link instead — Zyphr templates render URLs, not codes)
{{user.email_address}}{{user_email}}

From AWS Cognito

Cognito variableZyphr variable
{####} (verification code)(use a magic/reset link instead)
{username}{{user_email}}

Bulk import: one curl, all three templates

The bulk endpoint validates and saves all three templates atomically. If any one fails MJML compile or variable validation, none are saved — safe to retry after fixing the one that broke.

curl -X PUT https://api.zyphr.dev/v1/auth/email-templates \
-H "Content-Type: application/json" \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY" \
-d @templates.json

Where templates.json looks like:

{
"magic_link": {
"subject": "Sign in to {{application_name}}",
"html": "<!doctype html><html>...your branded HTML using {{magic_link_url}}...</html>",
"text": "Sign in: {{magic_link_url}}\n\nExpires in {{expires_minutes}} minutes.",
"from_name": "Acme Security",
"reply_to": "support@acme.com"
},
"password_reset": {
"subject": "Reset your {{application_name}} password",
"html": "...{{reset_url}}...",
"text": "Reset your password: {{reset_url}}"
},
"email_verification": {
"subject": "Verify your email",
"html": "...{{verify_url}}...",
"text": "Verify your email: {{verify_url}}"
}
}

A successful response returns the saved templates keyed by type. A failure response includes structured details:

{
"error": {
"code": "mjml_compile_failed",
"message": "MJML compilation failed",
"details": [
{ "line": 12, "message": "Unknown tag mj-foo", "tagName": "mj-foo" }
]
}
}

MJML import

If your existing emails are MJML, send the source in mjml_source instead of html. Zyphr compiles to HTML on save and stores both:

{
"password_reset": {
"subject": "Reset your password",
"mjml_source": "<mjml><mj-body>...{{reset_url}}...</mj-body></mjml>",
"text": "Reset your password: {{reset_url}}"
}
}

Fork the system default

If you don't have an existing template and just want to start from Zyphr's default and tweak it:

curl https://api.zyphr.dev/v1/auth/email-templates/password_reset/default \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY" \
> my_template.json

# edit my_template.json...

curl -X PUT https://api.zyphr.dev/v1/auth/email-templates/password_reset \
-H "Content-Type: application/json" \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY" \
-d @my_template.json

Verify before going live

Before flipping users over to Zyphr, preview and test send each template:

# Render with sample variables — returns subject/html/text, no send
curl -X POST https://api.zyphr.dev/v1/auth/email-templates/password_reset/preview \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY"

# Send a real test email (recipient must be in test_recipients allowlist)
curl -X POST https://api.zyphr.dev/v1/auth/email-templates/password_reset/test \
-H "Content-Type: application/json" \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY" \
-d '{ "to": "you@yourcompany.com" }'

Add addresses to applications.test_recipients via PATCH /v1/applications/:id (dashboard JWT) before calling the test endpoint.

Rollback

To revert a single type to the Zyphr system default:

curl -X DELETE https://api.zyphr.dev/v1/auth/email-templates/password_reset \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY"

To roll back to a previous version (after an update you regret):

# List versions
curl https://api.zyphr.dev/v1/auth/email-templates/password_reset/versions \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY"

# Restore version 3
curl -X POST https://api.zyphr.dev/v1/auth/email-templates/password_reset/versions/3/restore \
-H "X-Application-Key: $ZYPHR_PUBLIC_KEY" \
-H "X-Application-Secret: $ZYPHR_SECRET_KEY"

Project brand defaults

If you're not ready to author full templates and just want Zyphr's defaults to match your brand colors and logo, set the project-level brand defaults instead. The system defaults will pick them up automatically:

curl -X PATCH https://api.zyphr.dev/v1/projects/$PROJECT_ID/brand \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DASHBOARD_JWT" \
-d '{
"brand_primary_color": "#FF6B35",
"brand_secondary_color": "#004E64",
"brand_font_family": "Inter, -apple-system, sans-serif",
"brand_logo_url": "https://yourbrand.com/logo.png"
}'

This works for accounts with multiple brands — set brand defaults independently on each project.