> ## Documentation Index
> Fetch the complete documentation index at: https://docs.autosend.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Send emails with Better Auth

> Learn how to send authentication emails using AutoSend with Better Auth for email verification, password reset, and OTP.

export const APP_PATHS = {
  home: '/',
  quickstart: '/quickstart',
  domainConfiguration: '/domain',
  apiReference: '/api-reference',
  sendEmail: '/api-reference/mails/send',
  bulkSendEmail: '/api-reference/mails/bulk',
  upsertContactApiRef: '/api-reference/contacts/upsert-contact',
  transactional: '/transactional-emails',
  emailActivity: '/transactional-emails/email-activity',
  emailTemplates: '/transactional-emails/email-templates',
  sendingEmail: '/quickstart/email-using-api',
  transactionalTroubleshooting: '/transactional-emails/troubleshooting',
  marketing: '/marketing-emails',
  campaigns: '/marketing-emails/campaigns',
  contacts: '/marketing-emails/contacts',
  contactsIntroduction: '/marketing-emails/contacts/introduction',
  contactsImportCsv: '/marketing-emails/contacts/import-csv',
  contactsLists: '/marketing-emails/contacts/lists',
  contactsSegments: '/marketing-emails/contacts/segments',
  contactsCustomFields: '/marketing-emails/contacts/contact-properties',
  contactsContactProperties: '/marketing-emails/contacts/contact-properties',
  createContactPropertyApiRef: '/api-reference/contact-properties/create',
  listContactPropertiesApiRef: '/api-reference/contact-properties/list',
  getContactPropertyApiRef: '/api-reference/contact-properties/get-by-name',
  deleteContactPropertyApiRef: '/api-reference/contact-properties/delete',
  sender: '/marketing-emails/sender',
  unsubscribeGroups: '/others/unsubscribe-groups',
  webhookIntroduction: '/others/webhooks/introduction',
  webhookEventType: '/others/webhooks/event-type',
  webhookRetries: '/others/webhooks/retries',
  webhookVerifyRequests: '/others/webhooks/verify-requests',
  dynamicTemplates: '/dynamic-templates',
  guides: '/guides',
  sitemap: '/sitemap.xml',
  team: '/others/team',
  automations: '/automations',
  events: '/automations/events',
  sendEventApi: '/api-reference/events/send-event',
  smtpIntroduction: '/quickstart/smtp',
  betterAuth: '/guides/better-auth',
  convexGuide: '/guides/convex',
  templateVariables: '/transactional-emails/variables',
  suppressions: '/others/suppressions',
  rateLimit: '/api-reference/rate-limit',
  nodejsSdk: '/sdk/nodejs',
  smtpIntegrationGuides: '/guides/smtp',
  apiKeys: '/api-keys',
  encryptedPayloads: '/others/encrypted-payloads',
  apiReferenceIntroduction: '/api-reference/introduction',
  lovableGuide: '/ai/integrations/lovable',
  aiIntroduction: '/ai/introduction',
  aiSkills: '/ai/skills',
  aiMcpServer: '/ai/mcp-server',
  aiLovable: '/ai/integrations/lovable',
  aiBolt: '/ai/integrations/bolt',
  aiV0: '/ai/integrations/v0',
  aiReplit: '/ai/integrations/replit',
  mcpClaude: '/ai/mcp-clients/claude',
  mcpCursor: '/ai/mcp-clients/cursor',
  mcpCopilot: '/ai/mcp-clients/copilot',
  mcpWindsurf: '/ai/mcp-clients/windsurf',
  mcpCodex: '/ai/mcp-clients/codex',
  mcpAntigravity: '/ai/mcp-clients/antigravity',
  mcpChatgpt: '/ai/mcp-clients/chatgpt',
  mcpRaycast: '/ai/mcp-clients/raycast',
  domainWarmup: '/marketing-emails/domain-warmup',
  projects: '/projects',
  createAutomationApi: '/api-reference/automations/create-automation',
  migrationSendgrid: '/migration/sendgrid',
  migrationResend: '/migration/resend',
  auth0CustomAction: '/guides/auth0-custom-action',
  accountBilling: '/others/account/billing',
  accountUsage: '/others/account/usage',
  inboundIntroduction: '/inbound/introduction',
  listInboundMessagesApi: '/api-reference/inbound-emails/list-messages',
  getInboundMessageApi: '/api-reference/inbound-emails/get-message',
  downloadInboundAttachmentApi: '/api-reference/inbound-emails/download-attachment',
  replyToInboundMessageApi: '/api-reference/inbound-emails/reply-to-message',
  wikiDailySendingLimit: '/wiki/daily-sending-limit'
};

<Frame>
  <img src="https://mintcdn.com/autosend-13920f5c/146jG-YpguLOOf7R/images/smtp/autosendxbetterauth.jpg?fit=max&auto=format&n=146jG-YpguLOOf7R&q=85&s=4652a16dbdcaac5c6d87275593fa402b" alt="AutoSend x Better Auth" width="1800" height="600" data-path="images/smtp/autosendxbetterauth.jpg" />
</Frame>

This guide shows you how to integrate AutoSend with [Better Auth](https://www.better-auth.com) to send authentication emails. Better Auth is a framework-agnostic authentication library for TypeScript that supports email verification, password reset, and OTP-based authentication.

## Prerequisites

<CardGroup cols={2}>
  <Card title="Verified Domain" href="https://autosend.com/settings/domains">
    Make sure you have a verified domain added in AutoSend to send emails from.
  </Card>

  <Card title="API Key" href="https://autosend.com/settings/api-key">
    Create a new API key for SMTP authentication or use the existing one.
  </Card>
</CardGroup>

## Installation

Install Better Auth in your project if you haven't already:

```bash theme={null}
npm install better-auth
```

## Integration

<Steps>
  <Step title="Set Environment Variables" titleSize="h3">
    Add the required environment variables to your `.env` file:

    ```bash .env theme={null}
    AUTOSEND_API_KEY=your_autosend_api_key_here
    FROM_EMAIL=noreply@yourdomain.com
    FROM_NAME=Your App Name
    ```

    <Info>
      Get your API key from the [API Keys](https://autosend.com/settings/api-key) page in your AutoSend dashboard.
    </Info>
  </Step>

  <Step title="Create AutoSend Email Helper" titleSize="h3">
    Create a helper function to send emails via the <a href={APP_PATHS.sendEmail}>AutoSend API</a> using templates:

    ```typescript lib/autosend.ts theme={null}
    const AUTOSEND_API_KEY = process.env.AUTOSEND_API_KEY!;
    const FROM_EMAIL = process.env.FROM_EMAIL || "noreply@yourdomain.com";
    const FROM_NAME = process.env.FROM_NAME || "Your App";

    interface SendEmailOptions {
      to: string;
      templateId: string;
      dynamicData: Record<string, string>;
    }

    export async function sendEmail({ to, templateId, dynamicData }: SendEmailOptions) {
      const response = await fetch("https://api.autosend.com/v1/mails/send", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${AUTOSEND_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          from: {
            email: FROM_EMAIL,
            name: FROM_NAME,
          },
          to: {
            email: to,
          },
          templateId,
          dynamicData,
        }),
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.message || "Failed to send email");
      }

      return response.json();
    }
    ```
  </Step>

  <Step title="Create Email Templates" titleSize="h3">
    Create email templates in your [AutoSend dashboard](https://app.autosend.com) for each authentication flow:

    | Template           | Suggested ID          | Variables                             |
    | ------------------ | --------------------- | ------------------------------------- |
    | Email verification | `tmpl_verify_email`   | `{{userName}}`, `{{verificationUrl}}` |
    | Password reset     | `tmpl_password_reset` | `{{userName}}`, `{{resetUrl}}`        |
    | Sign-in OTP        | `tmpl_otp_signin`     | `{{otp}}`                             |
    | Verification OTP   | `tmpl_otp_verify`     | `{{otp}}`                             |
    | Password reset OTP | `tmpl_otp_reset`      | `{{otp}}`                             |

    Learn more about <a href={APP_PATHS.templateVariables}>template variables</a>.
  </Step>

  <Step title="Configure Better Auth" titleSize="h3">
    Wire up your Better Auth configuration to send emails through AutoSend:

    ```typescript lib/auth.ts theme={null}
    import { betterAuth } from "better-auth";
    import { emailOTP } from "better-auth/plugins";
    import { sendEmail } from "./autosend";

    export const auth = betterAuth({
      // ... your database and other configuration

      emailVerification: {
        sendOnSignUp: true,
        sendVerificationEmail: async ({ user, url }) => {
          sendEmail({
            to: user.email,
            templateId: "tmpl_verify_email",
            dynamicData: {
              userName: user.name || "there",
              verificationUrl: url,
            },
          });
        },
      },

      emailAndPassword: {
        enabled: true,
        sendResetPassword: async ({ user, url }) => {
          sendEmail({
            to: user.email,
            templateId: "tmpl_password_reset",
            dynamicData: {
              userName: user.name || "there",
              resetUrl: url,
            },
          });
        },
      },

      plugins: [
        emailOTP({
          sendVerificationOTP: async ({ email, otp, type }) => {
            const templateIds = {
              "sign-in": "tmpl_otp_signin",
              "email-verification": "tmpl_otp_verify",
              "forget-password": "tmpl_otp_reset",
            };

            sendEmail({
              to: email,
              templateId: templateIds[type],
              dynamicData: { otp },
            });
          },
        }),
      ],
    });
    ```

    <Warning>
      Don't await email sending to prevent timing attacks. This ensures response time doesn't reveal whether an email was sent.
    </Warning>
  </Step>
</Steps>

For client-side setup (sending OTPs, verifying codes), see the [Better Auth Email OTP documentation](https://www.better-auth.com/docs/plugins/email-otp).

## Serverless Considerations

When running on serverless platforms (Vercel, AWS Lambda, Cloudflare Workers), ensure email sending completes before the function terminates.

<Tabs>
  <Tab title="Vercel">
    Use `waitUntil` to ensure the email is sent:

    ```typescript theme={null}
    import { waitUntil } from '@vercel/functions';
    import { sendEmail } from './autosend';

    // In your Better Auth config
    sendVerificationEmail: async ({ user, url }) => {
      waitUntil(
        sendEmail({
          to: user.email,
          templateId: 'tmpl_verify_email',
          dynamicData: {
            userName: user.name || 'there',
            verificationUrl: url,
          },
        })
      );
    };
    ```
  </Tab>

  <Tab title="Cloudflare Workers">
    Use the `ctx.waitUntil` method:

    ```typescript theme={null}
    sendVerificationEmail: async ({ user, url }) => {
      // Access ExecutionContext from your request handler
      ctx.waitUntil(
        sendEmail({
          to: user.email,
          templateId: 'tmpl_verify_email',
          dynamicData: {
            userName: user.name || 'there',
            verificationUrl: url,
          },
        })
      );
    };
    ```
  </Tab>
</Tabs>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Domain not verified">
    If you receive a "Domain not verified" error, ensure your sending domain is properly configured in AutoSend.

    **Solution:** Go to your [Domain Settings](https://autosend.com/settings/domains) and complete the DNS verification process.
  </Accordion>

  <Accordion title="Invalid API key">
    A 401 error indicates your API key is invalid or missing.

    **Solution:**

    1. Check your `.env` file has the correct `AUTOSEND_API_KEY`
    2. Verify the key in your [API Keys](https://autosend.com/settings/api-key) dashboard
    3. Ensure the key hasn't been revoked
  </Accordion>

  <Accordion title="Rate limit exceeded">
    If you're sending too many emails, you may hit rate limits.

    **Solution:** Review the <a href={APP_PATHS.rateLimit}>Rate Limits</a> documentation and implement appropriate throttling in your application.
  </Accordion>

  <Accordion title="Email not delivered">
    If emails aren't being delivered:

    1. Check the [Email Activity](https://autosend.com/email-activities) dashboard for delivery status
    2. Verify the recipient email is valid
    3. Check if the email is in [Suppressions](https://autosend.com/suppressions/global)
    4. Review the <a href={APP_PATHS.transactionalTroubleshooting}>Troubleshooting Guide</a> for common issues
  </Accordion>
</AccordionGroup>

## Next Steps

<Columns cols={2}>
  <Card title="Email Templates" icon="https://mintcdn.com/autosend-13920f5c/nx_wYfWx3qeZwg1C/icons/email-templates.svg?fit=max&auto=format&n=nx_wYfWx3qeZwg1C&q=85&s=461e1cf135b49bcb45ed4373269d54b9" href={APP_PATHS.emailTemplates} width="24" height="24" data-path="icons/email-templates.svg">
    Create reusable, personalized email templates for transactional emails.
  </Card>

  <Card title="Webhooks" icon="https://mintcdn.com/autosend-13920f5c/nx_wYfWx3qeZwg1C/icons/webhook.svg?fit=max&auto=format&n=nx_wYfWx3qeZwg1C&q=85&s=14ad6675c71731ac04f786559a813ee1" href={APP_PATHS.webhookIntroduction} width="24" height="24" data-path="icons/webhook.svg">
    Use webhooks to notify your application about email and contact events in real-time.
  </Card>

  <Card title="Sending Domain" icon="https://mintcdn.com/autosend-13920f5c/nx_wYfWx3qeZwg1C/icons/domain.svg?fit=max&auto=format&n=nx_wYfWx3qeZwg1C&q=85&s=9393f9f9b0f7029e6ba8acf2bc09d864" href={APP_PATHS.domainConfiguration} width="24" height="24" data-path="icons/domain.svg">
    Here's a step-by-step guide on how to verify and authenticate domain on AutoSend for sending
    emails.
  </Card>

  <Card title="Email Activity" icon="https://mintcdn.com/autosend-13920f5c/nx_wYfWx3qeZwg1C/icons/email-activity.svg?fit=max&auto=format&n=nx_wYfWx3qeZwg1C&q=85&s=2ecad7369f217ee7d03c3d8dfdd36d22" href={APP_PATHS.emailActivity} width="24" height="24" data-path="icons/email-activity.svg">
    Track delivery, performance, and troubleshoot issues for your transactional emails.
  </Card>
</Columns>
