> ## 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 Supabase Edge Functions

> Learn how to send transactional emails from Supabase Edge Functions using AutoSend.

## Overview

This guide shows you how to integrate AutoSend with Supabase Edge Functions to send emails for authentication, notifications, and other transactional use cases. Edge Functions run at the edge, close to your users, making them perfect for handling email operations with low latency.

<Tip>
  AutoSend has an official [integration with
  Supabase](https://supabase.com/partners/integrations/AutoSend). For a less technical setup, you
  can also use [SMTP to integrate with Supabase](/guides/smtp/supabase).
</Tip>

## Prerequisites

Before you begin, make sure you have:

* An [AutoSend account](https://autosend.com/) with an API key
* A [Supabase project](https://supabase.com/)
* [Supabase CLI](https://supabase.com/docs/guides/cli#installation) installed (v1.0 or later)
* Node.js 18+ installed locally

## Quickstart

<Steps>
  <Step title="Create a Supabase Edge Function" titleSize="h3">
    Initialize your Supabase project and create a new Edge Function:

    ```bash theme={null}
    # Login to Supabase
    supabase login

    # Initialize your project (if not already done)
    supabase init

    # Create a new Edge Function
    supabase functions new autosend
    ```

    This creates a new function in `supabase/functions/autosend/index.ts`.
  </Step>

  <Step title="Install Dependencies" titleSize="h3">
    Edge Functions support importing from standard URLs. No package installation needed!
  </Step>

  <Step title="Write the Edge Function" titleSize="h3">
    Replace the content of `supabase/functions/autosend/index.ts` with:

    ```tsx expandable theme={null}
    import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
    import { corsHeaders } from '../_shared/cors.ts'

    serve(async (req) => {
      // Handle CORS preflight requests
      if (req.method === 'OPTIONS') {
        return new Response('ok', { headers: corsHeaders })
      }

      try {
        const { to, from, subject, html, templateId, dynamicData } = await req.json()

        // Get AutoSend API key from environment variables
        const autoSendApiKey = Deno.env.get('AUTOSEND_API_KEY')

        if (!autoSendApiKey) {
          throw new Error('AUTOSEND_API_KEY not configured')
        }

        // Prepare email payload
        const emailPayload: any = {
          to: {
            email: to.email,
            name: to.name || undefined
          },
          from: {
            email: from.email,
            name: from.name || undefined
          }
        }

        // Add template or HTML content
        if (templateId) {
          emailPayload.templateId = templateId
          if (dynamicData) {
            emailPayload.dynamicData = dynamicData
          }
        } else {
          emailPayload.subject = subject
          emailPayload.html = html
        }

        // Send email via AutoSend API
        const response = await fetch('https://api.autosend.com/v1/mails/send', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${autoSendApiKey}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(emailPayload)
        })

        const data = await response.json()

        if (!response.ok) {
          throw new Error(data.message || 'Failed to send email')
        }

        return new Response(
          JSON.stringify({
            success: true,
            emailId: data.data.emailId,
            status: data.data.status
          }),
          {
            headers: { ...corsHeaders, 'Content-Type': 'application/json' },
            status: 200,
          },
        )
      } catch (error) {
        return new Response(
          JSON.stringify({
            success: false,
            error: error.message
          }),
          {
            headers: { ...corsHeaders, 'Content-Type': 'application/json' },
            status: 400,
          },
        )
      }
    })
    ```
  </Step>

  <Step title="Create Shared CORS Configuration" titleSize="h3">
    Create a file at `supabase/functions/_shared/cors.ts`:

    ```tsx theme={null}
    export const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
    }
    ```
  </Step>

  <Step title="New Step" titleSize="h3">
    Create a `.env` file in your project root (for local development):

    ```bash theme={null}
    AUTOSEND_API_KEY=your_autosend_api_key_here
    ```

    For production, set secrets using the Supabase CLI:

    ```bash theme={null}
    supabase secrets set AUTOSEND_API_KEY=your_autosend_api_key_here
    ```
  </Step>

  <Step title="Run Locally" titleSize="h3">
    Test your function locally before deploying:

    ```bash theme={null}
    supabase functions serve autosend --env-file .env --no-verify-jwt
    ```

    The function will be available at: `http://localhost:54321/functions/v1/autosend`
  </Step>

  <Step title="Test Your Function" titleSize="h3">
    Send a test request using cURL:

    ```bash theme={null}
    curl -i --location --request POST 'http://localhost:54321/functions/v1/autosend' \
      --header 'Authorization: Bearer YOUR_SUPABASE_ANON_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
        "to": {
          "email": "[email protected]",
          "name": "John Doe"
        },
        "from": {
          "email": "[email protected]",
          "name": "Your App"
        },
        "subject": "Welcome to our platform!",
        "html": "<h1>Hello John!</h1><p>Welcome aboard!</p>"
      }'
    ```
  </Step>

  <Step title="Deploy to Production" titleSize="h3">
    Once tested, deploy your function:

    ```bash theme={null}
    supabase functions deploy autosend
    ```

    Your function will be available at:

    ```
    https://YOUR_PROJECT_REF.supabase.co/functions/v1/autosend
    ```
  </Step>
</Steps>

## Usage Examples

<AccordionGroup>
  <Accordion title="Send a Simple Email">
    ```jsx theme={null}
    const { data, error } = await supabase.functions.invoke('autosend', {
      body: {
        to: {
          email: '[email protected]',
          name: 'Jane Smith'
        },
        from: {
          email: '[email protected]',
          name: 'MyApp'
        },
        subject: 'Welcome!',
        html: '<h1>Welcome to MyApp!</h1><p>We are excited to have you.</p>'
      }
    })

    if (error) {
      console.error('Error sending email:', error)
    } else {
      console.log('Email sent:', data)
    }
    ```
  </Accordion>

  <Accordion title="Send Using AutoSend Template">
    ```jsx theme={null}
    const { data, error } = await supabase.functions.invoke('autosend', {
      body: {
        to: {
          email: '[email protected]',
          name: 'Jane Smith'
        },
        from: {
          email: '[email protected]',
          name: 'MyApp'
        },
        templateId: 'tmpl_welcome_email',
        dynamicData: {
          firstName: 'Jane',
          loginUrl: 'https://app.example.com/login'
        }
      }
    })
    ```
  </Accordion>

  <Accordion title="Send from React/Next.js">
    ```jsx theme={null}
    const sendEmail = async () => {
      const response = await fetch(
        'https://YOUR_PROJECT_REF.supabase.co/functions/v1/autosend',
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${supabaseAnonKey}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            to: {
              email: '[email protected]',
              name: 'John Doe'
            },
            from: {
              email: '[email protected]',
              name: 'MyApp'
            },
            subject: 'Order Confirmation',
            html: '<p>Your order has been confirmed!</p>'
          })
        }
      )

      const data = await response.json()
      return data
    }
    ```
  </Accordion>
</AccordionGroup>

## Integration with Supabase Auth

You can use AutoSend to send custom authentication emails by integrating with Supabase Auth Hooks.

<Steps>
  <Step title="Create Auth Hook Function" titleSize="h3">
    ```bash theme={null}
    supabase functions new send-auth-email
    ```
  </Step>

  <Step title="Implement Auth Hook Handler" titleSize="h3">
    ```tsx expandable theme={null}
    import { serve } from "https://deno.land/std@0.168.0/http/server.ts"

    serve(async (req) => {
      try {
        const payload = await req.json()
        const { event_type, user, token_hash, redirect_to } = payload

        const autoSendApiKey = Deno.env.get('AUTOSEND_API_KEY')

        let templateId: string
        let dynamicData: any = {}

        // Map event types to templates
        switch (event_type) {
          case 'user.signup':
            templateId = 'tmpl_signup_confirmation'
            dynamicData = {
              confirmationUrl: `${redirect_to}?token_hash=${token_hash}&type=signup`,
              email: user.email
            }
            break

          case 'user.password_recovery':
            templateId = 'tmpl_password_reset'
            dynamicData = {
              resetUrl: `${redirect_to}?token_hash=${token_hash}&type=recovery`,
              email: user.email
            }
            break

          case 'user.email_change':
            templateId = 'tmpl_email_change'
            dynamicData = {
              confirmationUrl: `${redirect_to}?token_hash=${token_hash}&type=email_change`,
              newEmail: user.new_email
            }
            break

          default:
            throw new Error(`Unsupported event type: ${event_type}`)
        }

        // Send email via AutoSend
        const response = await fetch('https://api.autosend.com/v1/mails/send', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${autoSendApiKey}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            to: {
              email: user.email
            },
            from: {
              email: '[email protected]',
              name: 'Your App'
            },
            templateId,
            dynamicData
          })
        })

        const data = await response.json()

        if (!response.ok) {
          throw new Error(data.message || 'Failed to send auth email')
        }

        return new Response(
          JSON.stringify({ success: true }),
          {
            headers: { 'Content-Type': 'application/json' },
            status: 200,
          },
        )
      } catch (error) {
        return new Response(
          JSON.stringify({ error: error.message }),
          {
            headers: { 'Content-Type': 'application/json' },
            status: 400,
          },
        )
      }
    })
    ```
  </Step>

  <Step title="Configure Auth Hook in Supabase" titleSize="h3">
    1. Go to **Authentication > Hooks** in your Supabase dashboard
    2. Enable the "Send Email" hook
    3. Set the hook URL to your deployed function:

       ```
       https://YOUR_PROJECT_REF.supabase.co/functions/v1/send-auth-email

       ```
    4. Configure the secret (optional but recommended)

    Now all Supabase Auth emails will be sent through AutoSend!
  </Step>
</Steps>

## Error Handling

Implement robust error handling for production:

```tsx expandable theme={null}
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';

serve(async (req) => {
	try {
		const payload = await req.json();

		// Validate required fields
		if (!payload.to?.email) {
			throw new Error('Recipient email is required');
		}

		if (!payload.from?.email) {
			throw new Error('Sender email is required');
		}

		const autoSendApiKey = Deno.env.get('AUTOSEND_API_KEY');

		if (!autoSendApiKey) {
			throw new Error('AutoSend API key not configured');
		}

		const response = await fetch('https://api.autosend.com/v1/mails/send', {
			method: 'POST',
			headers: {
				Authorization: `Bearer ${autoSendApiKey}`,
				'Content-Type': 'application/json',
			},
			body: JSON.stringify(payload),
		});

		const data = await response.json();

		// Handle AutoSend API errors
		if (!response.ok) {
			console.error('AutoSend API Error:', data);

			switch (response.status) {
				case 400:
					throw new Error(`Validation error: ${data.message}`);
				case 401:
					throw new Error('Invalid API key');
				case 403:
					throw new Error('Domain not verified or insufficient permissions');
				case 429:
					throw new Error('Rate limit exceeded. Please try again later.');
				default:
					throw new Error(data.message || 'Failed to send email');
			}
		}

		return new Response(
			JSON.stringify({
				success: true,
				emailId: data.data.emailId,
				status: data.data.status,
			}),
			{
				headers: { 'Content-Type': 'application/json' },
				status: 200,
			},
		);
	} catch (error) {
		console.error('Function Error:', error);

		return new Response(
			JSON.stringify({
				success: false,
				error: error.message,
			}),
			{
				headers: { 'Content-Type': 'application/json' },
				status: 400,
			},
		);
	}
});
```

## Best Practices

<AccordionGroup>
  <Accordion title="Environment Variables">
    Always use environment variables for sensitive data:

    ```tsx theme={null}
    // ✅ Good
    const apiKey = Deno.env.get('AUTOSEND_API_KEY')

    // ❌ Bad - Never hardcode
    const apiKey = 'as_123456789'
    ```
  </Accordion>

  <Accordion title="Validate Email Addresses">
    Validate email formats before sending:

    ```tsx theme={null}
    function isValidEmail(email: string): boolean {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
      return emailRegex.test(email)
    }

    if (!isValidEmail(payload.to.email)) {
      throw new Error('Invalid email address')
    }
    ```
  </Accordion>

  <Accordion title="Implement Rate Limiting">
    Protect your function from abuse:

    ```tsx theme={null}
    // Use Supabase Rate Limiting or implement custom logic
    const userRateLimit = await checkUserRateLimit(userId)
    if (!userRateLimit.allowed) {
      throw new Error('Rate limit exceeded')
    }
    ```
  </Accordion>

  <Accordion title="Use Verified Domains">
    Always send from verified domains in AutoSend to ensure deliverability. \
    [Learn how to verify domains →](/domain)
  </Accordion>
</AccordionGroup>

## Testing

### Test Function Locally

```bash theme={null}
# Start local development server
supabase functions serve send-email --env-file .env

# Test with curl
curl -i --location --request POST 'http://localhost:54321/functions/v1/send-email' \
  --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
  --header 'Content-Type: application/json' \
  --data '{
    "to": { "email": "[email protected]" },
    "from": { "email": "[email protected]" },
    "subject": "Test Email",
    "html": "<p>This is a test</p>"
  }'
```


## Related topics

- [How to send emails from Supabase with SMTP](/guides/smtp/supabase.md)
- [Integrate AutoSend with Lovable](/ai/integrations/lovable.md)
- [How to send email with AutoSend API](/quickstart/email-using-api.md)
- [How to send email with AutoSend SMTP](/quickstart/smtp.md)
- [Send emails with Better Auth](/guides/better-auth.md)
