Thêm thông báo Email cho website đặt lịch AI của bạn
Gửi email xác nhận lịch đặt và cảnh báo cho chủ doanh nghiệp bằng Supabase, Resend và Edge Functions.
Lộ trình & tài nguyên
Mới đến đây? Hãy bắt đầu từ chỗ này trước
Hướng dẫn này nâng cấp dự án Website đặt lịch AI với thông báo email tự động. Nếu bạn chưa xây dựng website đặt lịch, hãy bắt đầu từ hướng dẫn chính trước.
Xây dựng website đặt lịch bằng AI
Mở hướng dẫn chính
MAIN GUIDE
https://profitstudio.app/video/build-booking-website-ai
Thiết lập Resend cho email đặt lịch
Tạo và cấu hình tài khoản Resend của bạn
Mở Resend
https://resend.com/
Tạo tài khoản Resend Tạo khóa API Resend Sao chép và lưu khóa API một cách an toàn
QUAN TRỌNG
Giữ bí mật khóa API Resend. Đừng bao giờ thêm nó vào mã frontend hoặc chia sẻ công khai.
Thêm và xác minh tên miền gửi thư
Thêm tên miền website đặt lịch của bạn Chọn khu vực gần nhất Sao chép các bản ghi DNS từ Resend Thêm các bản ghi vào nhà cung cấp tên miền của bạn Quay lại Resend và xác nhận tên miền đã được xác minh
Việc xác minh DNS có thể mất vài phút hoặc lâu hơn, tùy vào nhà cung cấp tên miền của bạn.
Tạo hàm gửi email đặt lịch
Tạo Edge Function gửi email đặt lịch
Mở Supabase
https://supabase.com/?utm_source=partner&utm_medium=social&utm_campaign=supasquad&dub_id=faVfKdcLWvKCbYNp
Mở dự án Supabase của bạn Mở Edge Functions Tạo một function mới bằng trình soạn thảo trên trình duyệt Đặt tên function là `send-booking-email` Xóa đoạn mã khởi tạo mặc định
Tùy chỉnh và thêm mã của function
Nhập email của chủ sở hữu Nhập tên miền Resend đã được xác minh Nhập tên doanh nghiệp gửi thư Tùy chỉnh mẫu email nếu cần Sao chép và dán mã function đã tạo
Hàm gửi email đặt lịch
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" }, } ); } });
Email của chủ doanh nghiệp
owner@example.com
Tên miền Resend đã xác minh
yourdomain.com
Tên doanh nghiệp gửi thư
Phòng khám Nha khoa Bright
Chủ đề email gửi chủ doanh nghiệp
Tiêu đề email gửi chủ doanh nghiệp
Đoạn mở đầu email gửi chủ doanh nghiệp
Chủ đề email gửi khách hàng
Tiêu đề email gửi khách hàng
Đoạn mở đầu email gửi khách hàng
Đoạn kết email gửi khách hàng
Triển khai và cấu hình function
Triển khai function Mở phần cài đặt của function Tắt `Verify JWT With Legacy Secret` Lưu các thay đổi Sao chép và lưu URL endpoint của function để thiết lập webhook
QUAN TRỌNG
Tắt “Verify JWT With Legacy Secret” để Database Webhook có thể gọi function.
Thêm khóa API Resend vào Supabase
Thêm khóa API Resend làm Secret trong Supabase
Mở Edge Function Secrets Tạo một secret mới Đặt tên là `RESEND_API_KEY` Dán khóa API Resend vào Lưu secret
QUAN TRỌNG
Dùng đúng tên secret `RESEND_API_KEY`. Chỉ cần sai một ký tự là function sẽ không truy cập được Resend.
Kết nối email với các lịch đặt mới
Tạo webhook gửi email đặt lịch
Kích hoạt function gửi email đặt lịch mỗi khi có lịch hẹn mới được thêm vào.
Integrations Database Webhooks
Mở Database Webhooks Bật Database Webhooks Tạo một webhook mới Đặt tên là `send-booking-email-webhook` Chọn bảng `public.appointments` Chọn `Insert` làm sự kiện Giữ nguyên kiểu là `HTTP Request` Dùng `POST` làm phương thức request Dán URL endpoint của Edge Function Tạo webhook
Kiểm thử hệ thống email đặt lịch
Kiểm tra toàn bộ luồng email đặt lịch
Kiểm tra toàn bộ luồng đặt lịch và xác nhận cả hai email đều được gửi đúng.
Mở website đặt lịch đang chạy Gửi một lịch hẹn thử nghiệm Xác nhận lịch hẹn đã được lưu trong Supabase Kiểm tra email xác nhận gửi cho khách hàng Kiểm tra email thông báo gửi cho chủ sở hữu Xác minh thông tin lịch hẹn chính xác trong cả hai email Xác nhận lịch hẹn xuất hiện trong bảng điều khiển quản trị