Skip to main content

Documentation Index

Fetch the complete documentation index at: https://stackauth-e0affa27-chore-move-mcp-to-a-sep-app.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Stack Auth includes a full email system for sending transactional and marketing emails to your users. It handles rendering, delivery, scheduling, notification preferences, and tracking out of the box.

Email types

There are two categories of email:
  • Transactional - Required for your app to function (verification, password reset, receipts). Users cannot opt out.
  • Marketing - Promotional or informational. Always includes an unsubscribe link. Users can opt out.
Never send marketing content as transactional emails. Doing so can get your domain blacklisted by spam filters.

Sending emails

Emails are sent from your server using stackServerApp.sendEmail(). You must provide the content (HTML, a template, or a draft) and the recipients.

Send to specific users

await stackServerApp.sendEmail({
  userIds: ['user-id-1', 'user-id-2'],
  subject: 'Welcome to our platform!',
  html: '<h1>Welcome!</h1><p>Thanks for joining us.</p>',
});

Send to all users

await stackServerApp.sendEmail({
  allUsers: true,
  templateId: 'your-template-id',
  subject: 'We just shipped a big update',
  variables: {
    featureName: 'Dark mode',
  },
});

Send from a dashboard draft

If you’ve composed an email in the dashboard’s draft editor, you can trigger it programmatically:
await stackServerApp.sendEmail({
  userIds: ['user-id'],
  draftId: 'your-draft-id',
});

Full options

await stackServerApp.sendEmail({
  // Recipients  - exactly one of these is required:
  userIds: ['user-id-1'],     // specific users
  // allUsers: true,           // or all users in your project

  // Content  - exactly one of these is required:
  html: '<p>Hello!</p>',      // raw HTML
  // templateId: 'template-id', // or a template with variables
  // draftId: 'draft-id',       // or a dashboard draft

  // Optional:
  subject: 'Hello!',
  variables: { key: 'value' },
  themeId: 'theme-id',          // apply a specific theme
  // themeId: null,              // use the default theme
  // themeId: false,             // send with no theme at all
  notificationCategoryName: 'Marketing',
  scheduledAt: new Date('2025-12-25T00:00:00Z'),
});

SendEmailOptions type shape

type SendEmailOptions = {
  userIds: string[];                 // users to send to
  themeId?: string | null | false;   // theme override
  subject?: string;                  // subject line
  notificationCategoryName?: string; // preference category
  html?: string;                     // raw HTML body
  templateId?: string;               // template ID
  variables?: Record<string, any>;   // template variables
};
sendEmail requires a custom email server (SMTP, Resend, or Managed). It cannot be used with the shared development server.

Error handling

sendEmail returns a result object. Handle failures explicitly:
const result = await stackServerApp.sendEmail({
  userIds: ['user-id'],
  html: '<p>Hello!</p>',
  subject: 'Test Email',
});

if (result.status === 'error') {
  switch (result.error.code) {
    case 'REQUIRES_CUSTOM_EMAIL_SERVER':
      console.error('Please configure a custom email server');
      break;
    case 'SCHEMA_ERROR':
      console.error('Invalid email data provided');
      break;
    case 'USER_ID_DOES_NOT_EXIST':
      console.error('One or more user IDs do not exist');
      break;
  }
}

Scheduling

Pass a scheduledAt date to delay delivery. The email enters the pipeline immediately but won’t be sent until the scheduled time.
await stackServerApp.sendEmail({
  userIds: ['user-id'],
  html: '<p>Happy New Year!</p>',
  subject: 'Happy New Year!',
  scheduledAt: new Date('2026-01-01T00:00:00Z'),
});
If scheduledAt is omitted, the email is sent as soon as possible.

Email pipeline

Emails are processed asynchronously through a multi-stage pipeline:
  1. Enqueue - The email is saved to the outbox with its template, recipients, and scheduling metadata.
  2. Render - The template TSX is compiled into HTML, subject, and plain text.
  3. Queue - Rendered emails whose scheduled time has passed are queued for delivery, respecting your project’s sending capacity.
  4. Send - Emails are delivered, honoring notification preferences and skipping users who have unsubscribed.
  5. Track - Delivery events (sent, opened, clicked, bounced, marked as spam) are recorded.
You can monitor every email’s status in the dashboard under Emails → Sent.

Templates

Templates are React Email components written in TSX. Each template receives the current user, project, and any custom variables you pass when sending.
import { type } from "arktype";
import { Container } from "@react-email/components";
import { Subject, NotificationCategory, Props } from "@stackframe/emails";

export const variablesSchema = type({
  featureName: "string",
});

export function EmailTemplate({
  user,
  project,
  variables,
}: Props<typeof variablesSchema.infer>) {
  return (
    <Container>
      <Subject value={`New feature: ${variables.featureName}`} />
      <NotificationCategory value="Transactional" />
      <p>Hi {user.displayName}, check out {variables.featureName}!</p>
    </Container>
  );
}

EmailTemplate.PreviewVariables = {
  featureName: "Dark mode",
} satisfies typeof variablesSchema.infer;
Key concepts:
  • variablesSchema - Define the shape of your template variables using arktype. Stack Auth validates variables against this schema at render time.
  • <Subject> - Sets the email subject line from inside the template.
  • <NotificationCategory> - Declares whether this is a "Transactional" or "Marketing" email.
  • PreviewVariables - Sample data used for the live preview in the dashboard editor.

Built-in templates

Stack Auth ships with templates for common auth flows. These are used automatically by the built-in authentication components:
TemplateTrigger
Email VerificationUser signs up or changes their email
Password ResetUser requests a password reset
Magic Link / OTPUser signs in with magic link or one-time password
Team InvitationUser is invited to join a team
Sign-in InvitationUser is invited to create an account
Payment ReceiptA payment succeeds (one-time or subscription)
Payment FailedA payment fails
You can customize any built-in template from the dashboard under Emails → Templates.

Themes

Themes wrap your email content in a consistent layout - header, footer, background, branding. Stack Auth includes three built-in themes:
  • Default Light - Clean white background with subtle shadow
  • Default Dark - Dark background with light text
  • Default Colorful - Light purple background with an accent border
You can create custom themes in the dashboard under Emails → Email Settings → Themes. Themes are also TSX components:
import { Html, Head, Tailwind, Body, Container } from "@react-email/components";
import { ThemeProps, ProjectLogo } from "@stackframe/emails";

export function EmailTheme({ children, unsubscribeLink, projectLogos }: ThemeProps) {
  return (
    <Html>
      <Head />
      <Tailwind>
        <Body className="bg-white font-sans m-0 p-0">
          <Container className="max-w-[600px] mx-auto p-8">
            <ProjectLogo data={projectLogos} mode="light" />
            {children}
          </Container>
          {unsubscribeLink && (
            <p className="text-center text-xs opacity-60">
              <a href={unsubscribeLink}>Unsubscribe</a>
            </p>
          )}
        </Body>
      </Tailwind>
    </Html>
  );
}
Set a default theme for your project in the dashboard. You can also override the theme per-email with the themeId option, or pass themeId: false to send without any theme.

Notification preferences

Emails are categorized as either Transactional or Marketing. Users can opt out of Marketing emails but not Transactional ones. When sending, specify the category:
await stackServerApp.sendEmail({
  userIds: ['user-id'],
  html: '<p>Check out our new feature!</p>',
  subject: 'Product Updates',
  notificationCategoryName: 'Marketing',
});
If a user has unsubscribed from Marketing emails, the email will be automatically skipped during delivery. Marketing emails always include an unsubscribe link.

React components integration

Emails integrate with Stack Auth UI components automatically (for example verification, password reset, and magic-link flows).
For custom flows, trigger sendEmail from your server code:
import { stackServerApp } from '@stackframe/stack';

export async function inviteUser(userId: string) {
  const result = await stackServerApp.sendEmail({
    userIds: [userId],
    templateId: 'invitation-template',
    subject: "You're invited!",
    variables: {
      inviteUrl: 'https://yourapp.com/invite/token123',
    },
  });

  return result;
}

Email server configuration

Configure your email server in the dashboard under Emails → Email Settings. There are four options:

Shared (development only)

The default for new projects. Emails are sent from noreply@stackframe.co using Stack Auth’s shared infrastructure. Good for development - not suitable for production.

Custom SMTP

Connect any SMTP provider. Configure:
  • Host - e.g. smtp.sendgrid.net
  • Port - typically 587 (STARTTLS) or 465 (implicit TLS)
  • Username and Password
  • Sender email and Sender name

Resend

Connect your Resend account by entering your API key. Stack Auth configures the SMTP connection automatically.

Managed

Let Stack Auth manage your email domain. Stack Auth handles DNS configuration and deliverability for you. Set up requires:
  1. Choose a subdomain (e.g. mail.yourapp.com)
  2. Add the DNS records Stack Auth provides
  3. Verify the domain in the dashboard
The dashboard tests your email configuration automatically when you save it by sending a test email.

Delivery stats

Stack Auth tracks delivery metrics across multiple time windows (hour, day, week, month):
  • Sent - Successfully delivered
  • Bounced - Rejected by the recipient’s mail server
  • Marked as spam - Recipient flagged the email
Access these programmatically:
const info = await stackServerApp.getEmailDeliveryStats();
// info.stats.day.sent, info.stats.day.bounced, etc.
// info.capacity.rate_per_second, info.capacity.is_boost_active, etc.
Delivery capacity is managed automatically based on your sending reputation. If you need to temporarily increase throughput, you can activate a capacity boost:
await stackServerApp.activateEmailCapacityBoost();

Drafts

The dashboard includes a full draft editor where you can compose emails visually before sending. Drafts support:
  • TSX source editing with live preview
  • Theme selection
  • Recipient picker (specific users or all users)
  • Scheduling
  • Send history per draft
Once a draft is sent, it’s marked as sent and its delivery can be tracked in the outbox.