Skip to main content

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.

Prerequisites

Before you begin, make sure you have:

Quickstart

1

Create a Supabase Edge Function

Initialize your Supabase project and create a new Edge Function:
# 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.
2

Install Dependencies

Edge Functions support importing from standard URLs. No package installation needed!
3

Write the Edge Function

Replace the content of supabase/functions/autosend/index.ts with:
import { serve } from "https://deno.land/[email protected]/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,
      },
    )
  }
})
4

Create Shared CORS Configuration

Create a file at supabase/functions/_shared/cors.ts:
export const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
5

New Step

Create a .env file in your project root (for local development):
AUTOSEND_API_KEY=your_autosend_api_key_here
For production, set secrets using the Supabase CLI:
supabase secrets set AUTOSEND_API_KEY=your_autosend_api_key_here
6

Run Locally

Test your function locally before deploying:
supabase functions serve autosend --env-file .env --no-verify-jwt
The function will be available at: http://localhost:54321/functions/v1/autosend
7

Test Your Function

Send a test request using cURL:
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>"
  }'
8

Deploy to Production

Once tested, deploy your function:
supabase functions deploy autosend
Your function will be available at:
https://YOUR_PROJECT_REF.supabase.co/functions/v1/autosend

Usage Examples

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)
}
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'
    }
  }
})
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
}

Integration with Supabase Auth

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

Create Auth Hook Function

supabase functions new send-auth-email
2

Implement Auth Hook Handler

import { serve } from "https://deno.land/[email protected]/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,
      },
    )
  }
})
3

Configure Auth Hook in Supabase

  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!

Error Handling

Implement robust error handling for production:
import { serve } from "https://deno.land/[email protected]/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

Always use environment variables for sensitive data:
// ✅ Good
const apiKey = Deno.env.get('AUTOSEND_API_KEY')

// ❌ Bad - Never hardcode
const apiKey = 'as_123456789'
Validate email formats before sending:
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')
}
Protect your function from abuse:
// Use Supabase Rate Limiting or implement custom logic
const userRateLimit = await checkUserRateLimit(userId)
if (!userRateLimit.allowed) {
  throw new Error('Rate limit exceeded')
}
Always send from verified domains in AutoSend to ensure deliverability.
Learn how to verify domains →

Testing

Test Function Locally

# 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>"
  }'