Add Email Notifications to Your AI Booking Website
Send booking confirmation emails and owner alerts using Supabase, Resend, and Edge Functions.
Roadmap & Resources
New Here? Start Here First
This guide upgrades the AI Booking Website project with automatic email notifications. If you haven’t built the booking website yet, start with the main guide first.
Build a Booking Website with AI
Open the main guide
MAIN GUIDE
https://profitstudio.app/video/build-booking-website-ai
Set Up Resend for Booking Emails
Create and Configure Your Resend Account
Open Resend
https://resend.com/
Create a Resend Account Generate a Resend API Key Copy and Save the API Key Securely
IMPORTANT
Keep the Resend API key private. Never add it to frontend code or share it publicly.
Add and Verify Your Sending Domain
Add Your Booking Website Domain Select the Closest Region Copy the DNS Records From Resend Add the Records to Your Domain Provider Return to Resend and Confirm the Domain Is Verified
DNS verification may take a few minutes or longer depending on your domain provider.
Create the Booking Email Function
Create the Booking Email Edge Function
Open Supabase
https://supabase.com/?utm_source=partner&utm_medium=social&utm_campaign=supasquad&dub_id=faVfKdcLWvKCbYNp
Open Your Supabase Project Open Edge Functions Create a New Function Using the Browser Editor Name the Function `send-booking-email` Remove the Default Starter Code
Customize and Add the Function Code
Enter the Owner Email Enter the Verified Resend Domain Enter the Sender Business Name Customize the Email Templates if Needed Copy and Paste the Generated Function Code
Booking Email Function
import { serve } from "https://deno.land/std/http/server.ts"; serve(async (req) => { try { const body = await req.json(); const record = body.record || {}; const resendKey = Deno.env.get("RESEND_API_KEY"); const supabaseUrl = Deno.env.get("SUPABASE_URL"); const supabaseServiceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"); if (!resendKey) { return new Response( JSON.stringify({ error: "Missing RESEND_API_KEY secret" }), { status: 500, headers: { "Content-Type": "application/json" }, } ); } const ownerEmail = "YOUR_OWNER_EMAIL"; const customerEmail = record.email || ""; const customerName = record.full_name || record.name || "Customer"; const phone = record.phone || ""; const appointmentDate = record.appointment_date || record.date || ""; const startTime = record.start_time || record.time || ""; const endTime = record.end_time || ""; const notes = record.notes || ""; const status = record.status || "New"; let serviceName = record.service_name || record.service || record.service_title || ""; // If the appointment only has service_id, fetch the service name from the services table if (!serviceName && record.service_id && supabaseUrl && supabaseServiceRoleKey) { try { const serviceId = encodeURIComponent(record.service_id); const serviceResponse = await fetch( `${supabaseUrl}/rest/v1/services?id=eq.${serviceId}&select=name`, { method: "GET", headers: { apikey: supabaseServiceRoleKey, Authorization: `Bearer ${supabaseServiceRoleKey}`, "Content-Type": "application/json", }, } ); const serviceData = await serviceResponse.json(); if (Array.isArray(serviceData) && serviceData.length > 0) { serviceName = serviceData[0].name || ""; } } catch (_serviceError) { serviceName = ""; } } if (!serviceName) { serviceName = "Appointment"; } // 1) Email to business owner const ownerResponse = await fetch("https://api.resend.com/emails", { method: "POST", headers: { Authorization: `Bearer ${resendKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: "Bookings <bookings@YOUR_DOMAIN_NAME>", to: [ownerEmail], subject: "OWNER_NOTIFICATION_SUBJECT", html: ` <h2>OWNER_NOTIFICATION_HEADING</h2> <p>OWNER_NOTIFICATION_INTRO</p> <p><b>Customer Name:</b> ${customerName}</p> <p><b>Email:</b> ${customerEmail}</p> <p><b>Phone:</b> ${phone}</p> <p><b>Service:</b> ${serviceName}</p> <p><b>Date:</b> ${appointmentDate}</p> <p><b>Start Time:</b> ${startTime}</p> <p><b>End Time:</b> ${endTime}</p> <p><b>Notes:</b> ${notes}</p> <p><b>Status:</b> ${status}</p> `, }), }); const ownerData = await ownerResponse.json(); // 2) Email to customer let customerData = null; if (customerEmail) { const customerResponse = await fetch("https://api.resend.com/emails", { method: "POST", headers: { Authorization: `Bearer ${resendKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: "YOUR_BUSINESS_NAME <bookings@YOUR_DOMAIN_NAME>", to: [customerEmail], subject: "CUSTOMER_CONFIRMATION_SUBJECT", html: ` <h2>CUSTOMER_CONFIRMATION_HEADING</h2> <p>Hi ${customerName},</p> <p>CUSTOMER_CONFIRMATION_INTRO</p> <p><b>Service:</b> ${serviceName}</p> <p><b>Date:</b> ${appointmentDate}</p> <p><b>Start Time:</b> ${startTime}</p> <p><b>End Time:</b> ${endTime}</p> <p>If you need to make any changes, please contact us before your appointment.</p> <p>CUSTOMER_CONFIRMATION_CLOSING</p> `, }), }); customerData = await customerResponse.json(); } return new Response( JSON.stringify({ success: true, ownerEmailSent: ownerData, customerEmailSent: customerData, }), { headers: { "Content-Type": "application/json" }, } ); } catch (error) { return new Response( JSON.stringify({ success: false, error: String(error), }), { status: 500, headers: { "Content-Type": "application/json" }, } ); } });
Owner Email
owner@example.com
Verified Resend Domain
yourdomain.com
Sender Business Name
Bright Dental Clinic
Owner Email Subject
Owner Email Heading
Owner Email Intro Text
Customer Email Subject
Customer Email Heading
Customer Email Intro Text
Customer Email Closing Text
Deploy and Configure the Function
Deploy the Function Open the Function Settings Turn Off `Verify JWT With Legacy Secret` Save the Changes Copy and Save the Function Endpoint URL for the Webhook Setup
IMPORTANT
Turn off “Verify JWT With Legacy Secret” so the Database Webhook can call the function.
Add the Resend API Key to Supabase
Add the Resend API Key as a Supabase Secret
Open Edge Function Secrets Create a New Secret Set the Name to `RESEND_API_KEY` Paste the Resend API Key Save the Secret
IMPORTANT
Use the exact secret name `RESEND_API_KEY`. Any typo will prevent the function from accessing Resend.
Connect Emails to New Bookings
Create the Booking Email Webhook
Trigger the booking email function whenever a new appointment is added.
Integrations Database Webhooks
Open Database Webhooks Enable Database Webhooks Create a New Webhook Name It `send-booking-email-webhook` Select the `public.appointments` Table Choose `Insert` as the Event Keep `HTTP Request` as the Type Use `POST` as the Request Method Paste the Edge Function Endpoint URL Create the Webhook
Test the Booking Email System
Test the Complete Booking Email Flow
Test the complete booking flow and confirm that both emails are delivered correctly.
Open the Live Booking Website Submit a Test Appointment Confirm the Booking Is Saved in Supabase Check the Customer Confirmation Email Check the Owner Notification Email Verify the Booking Details Are Correct in Both Emails Confirm the Booking Appears in the Admin Dashboard