BluAuth
Docs
Sign in
User FAQ
  • Reset my password
  • I can't sign in
  • Didn't get reset email
  • Account linking
  • Session expiry
  • Two-factor auth
Admin Guides
Theme Studio
  • Overview
  • Layouts
  • Styling tokens
  • Concept copy
  • Assets & backgrounds
  • Advanced CSS
Admin Shell
  • Users
  • Providers
  • Clients
  • Invitations
Integrations
  • OIDC flow
  • Legacy OAuth flow
  • Provider token brokering
  • Email triggers
  • Webhook events
  • Session contract
Reference
  • API
  • Error codes
  • Event shapes
  • Design tokens
Runbooks
  • Deployment
  • Local operations

Email Triggers

BluAuth owns every auth-domain email sent in the Blu ecosystem: invitations, password resets, welcome emails, and (roadmap) email verification. Downstream apps do not send these emails themselves — they trigger a BluAuth operation which causes BluAuth to send the email, themed for the tenant that initiated the action.

This page describes each trigger, the template that renders it, the variables substituted, how to override the template per-tenant via Theme Studio, how to test without spamming real inboxes, and the SES/DNS requirements for custom sender domains.

Delivery pipeline

  1. A BluAuth operation triggers an email (invitation created, password-reset requested, signup completed).
  2. BluAuth resolves the tenant theme for the triggering client (resolveEmailTheme(clientId)).
  3. The template renders themed HTML + plain-text bodies via renderEmailBase + per-concept template module.
  4. sendEmail() dispatches through AWS SES v2 (SendEmailCommand) with the configured From address and SES configuration set.

Source: server/utils/email/send.ts, server/utils/email/resolve-theme.ts, shared/utils/email/templates/.

The four emails

EmailWhen it sendsTemplateSender
InvitationAdmin creates an invitation for a new usershared/utils/email/templates/invitation.tsBLUAUTH_SES_FROM_ADDRESS, display name = theme name
Password ResetUser submits /forgot-passwordshared/utils/email/templates/password-reset.tsBLUAUTH_SES_FROM_ADDRESS, display name = theme name
WelcomeUser accepts invitation or completes self-signupshared/utils/email/templates/welcome.tsBLUAUTH_SES_FROM_ADDRESS, display name = theme name
Email verification (roadmap)User changes their primary email——

All four emails route through the same sendEmail() helper, the same themed base chrome, and the same SES configuration set. They differ only in subject line, body HTML, and CTA link.

Template variables

Each template renders from a small, explicit set of inputs. No user-controlled HTML is interpolated.

Invitation — renderInvitationEmail

VariableExampleNotes
themeResolved tenant theme objectDrives logo, primary color, font preset, footer text, tenant display name.
acceptUrlhttps://auth.example.com/invite?token=<opaque>Single-use invitation link. Token valid until expiresAt.
clientName"Media Conductor"Used in the headline copy ("You're invited to Media Conductor").
recipientName"Jane Doe" or nullGreeting line. Falls back to "Hi there" when null.
inviterName"Clay Levering"Personalizes the body ("Clay invited you").
expiresAtDateRendered as a human-friendly expiry.

Password reset — renderPasswordResetEmail

VariableExampleNotes
themeResolved tenant theme objectChrome and branding.
resetUrlhttps://auth.example.com/reset-password-token?token=<opaque>Single-use link, 1-hour default TTL.
userName"Jane" or nullGreeting.

Welcome — renderWelcomeEmail

VariableExampleNotes
themeResolved tenant theme objectChrome and branding.
signInUrlhttps://auth.example.com/loginCTA link.
userName"Jane" or nullGreeting.

Theme resolution

At send time, BluAuth resolves the theme for the triggering client:

  1. Look up the OAuth client by ID.
  2. Load the client_themes row bound to the client.
  3. Fall back to the service-default theme if no client-bound theme exists.
  4. Fall back to HOSTED_LOGIN_THEME_DEFAULTS for any missing fields.

Resolved values that affect email rendering:

  • name — tenant display name; appears as the SES From display name and in subject copy.
  • primaryColor — hex color for CTA buttons and accent strokes.
  • logoUrl — tenant logo rendered in the email header.
  • footerText — legal/support line in the email footer.
  • fontFamilyPreset — web-safe stack referenced in the email stylesheet.

Every email BluAuth sends is theme-accurate for the tenant initiating the action. Changes made in Theme Studio (/admin/themes) take effect on the next send without redeploy.

Triggering from downstream apps

You don't invoke the email sender directly. You trigger a BluAuth operation that causes BluAuth to send.

Trigger an invitation email

POST https://auth.example.com/api/admin/invitations
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "email": "invitee@example.com",
  "recipientName": "First Last",
  "clientId": "7f9b0e7e-...-a11c",
  "expiresAt": "2026-04-23T00:00:00.000Z"
}

BluAuth creates the invitations row, publishes invitation.created, resolves the theme for clientId, and sends the invitation email via SES. The invitation email is not sent on invitation.revoked or invitation.accepted — only on creation.

Trigger a password-reset email

POST https://auth.example.com/api/auth/forgot-password
Content-Type: application/json

{
  "email": "user@example.com"
}

This endpoint is exposed through the Better Auth catch-all at server/api/auth/[...all].ts. BluAuth creates a reset token, persists it, and sends the themed reset email. The handler always returns 200 — even when the address does not match a user — to prevent account enumeration.

Trigger a welcome email

You don't trigger it directly. BluAuth sends the welcome email automatically when:

  • A user accepts an invitation at /api/auth/accept-invite.
  • A user completes self-signup (where self-signup is enabled on the client).

If your app's sign-up flow creates a BluAuth user, the welcome email fires automatically. Don't send your own in parallel — users will receive duplicates.

What downstream apps should do

  • Don't duplicate these emails. BluAuth owns the invitation, password-reset, and welcome templates. If your app wants to send a different welcome-to-the-product sequence after the auth welcome lands, subscribe to the user.created or invitation.accepted webhook and drive your sequence from there.
  • Do subscribe to webhooks (webhook events) when you need to react to these lifecycle events — e.g. to create a tenant record in your app the moment a user signs up.
  • Do customize your theme in /admin/themes. Every email inherits the theme bound to the initiating client, including logo, colors, and footer copy.

Previewing and testing templates

Admins can preview and test-send any template from /admin/settings → Email test (backed by POST /api/admin/email-test).

Preview only (renders HTML, no SES send)

POST https://auth.example.com/api/admin/email-test
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "themeId": "b1f2c3d4-...-0001",
  "recipientEmail": "qa@example.com",
  "previewOnly": true,
  "emailConcept": "invitation"
}

Response:

{
    "success": true,
    "data": {
        "html": "<!doctype html>...",
        "text": "You're invited...",
        "theme": "Media Conductor",
        "emailConcept": "invitation"
    }
}

The admin UI renders this HTML in a sandboxed iframe so you can review layout, colors, logo, and copy without sending anything.

Test send (SES send to a real inbox)

Omit previewOnly (or set it to false):

{
    "themeId": "b1f2c3d4-...-0001",
    "recipientEmail": "qa-inbox@example.com",
    "emailConcept": "password-reset"
}

The test send:

  • Uses the real SES send path — DKIM/SPF alignment, bounce handling, and configuration-set routing are all exercised.
  • Substitutes placeholder tokens (test-preview-token-000) in CTA URLs. The links won't actually work — they're for visual QA only.
  • Subjects are prefixed [TEST] so they're easy to filter in inboxes.

emailConcept accepts "password-reset", "welcome", or "invitation". The test route is admin-only.

Source: server/api/admin/email-test.post.ts.

Sender identity

  • All emails send from BLUAUTH_SES_FROM_ADDRESS (env var), e.g. no-reply@auth.example.com.
  • The SES From header uses the format <theme.name> <from@domain> so the user sees "Media Conductor" rather than "BluAuth" when the triggering tenant has a themed name.
  • Every message tags source_app=bluauth via SES EmailTags for cost attribution and log filtering.
  • The configuration set (env var BLUAUTH_SES_CONFIGURATION_SET) routes bounce / complaint / delivery events to CloudWatch.

DKIM / SPF / DMARC for custom sender domains

If your tenant uses a custom sender domain (e.g. no-reply@mail.tenant.com instead of the shared BluAuth domain):

  • Verify the domain in SES. The domain must be a verified identity in the AWS account BluAuth runs in.
  • Publish the DKIM CNAMEs. SES generates three CNAMEs; all three must resolve.
  • Align SPF. Add include:amazonses.com to the domain's SPF record. SPF alignment on the From domain is required for DMARC pass.
  • Publish a DMARC record. Start with p=none and graduate to p=quarantine once you've verified alignment in SES + DMARC aggregate reports.
  • Do not send from unverified domains. SES rejects at send time.

Ask the BluAuth admin to update BLUAUTH_SES_FROM_ADDRESS (or introduce per-tenant senders once that's supported) after the DNS records propagate.

Delivery reliability

  • SES bounces and complaints are recorded by SES and routed to the configuration set's event destinations (CloudWatch + SNS). They are not yet surfaced in the BluAuth admin UI — persistent bounces should be investigated in the SES console.
  • BluAuth does not automatically pause invitations to a bouncing address. Admins should check SES's suppression list before re-inviting a bouncing user.
  • Test sends are subject to the same bounce/complaint accounting as real sends. Use disposable test inboxes.

Localization

Not implemented — all emails are English. Localization is gated on the first tenant with a non-English primary locale. When added, locale will be resolved from the recipient's profile locale field, falling back to the tenant default, falling back to en-US.

Logging

Every send emits a structured log line with the template name, theme name, recipient email (hashed), SES message ID, and duration. Failures include the SES error code. Search the BluAuth logs with email.concept=<concept> to audit what's been sent to whom.

On this page

  • Delivery pipeline
  • The four emails
  • Template variables
  • Invitation — renderInvitationEmail
  • Password reset — renderPasswordResetEmail
  • Welcome — renderWelcomeEmail
  • Theme resolution
  • Triggering from downstream apps
  • Trigger an invitation email
  • Trigger a password-reset email
  • Trigger a welcome email
  • What downstream apps should do
  • Previewing and testing templates
  • Preview only (renders HTML, no SES send)
  • Test send (SES send to a real inbox)
  • Sender identity
  • DKIM / SPF / DMARC for custom sender domains
  • Delivery reliability
  • Localization
  • Logging
DocsPrivacyTerms
© 2026 Blu Digital Group