Añade notificaciones por email a tu sitio de reservas con IA
Envía emails de confirmación de reserva y alertas al propietario usando Supabase, Resend y Edge Functions.
Hoja de ruta y recursos
¿Nuevo? Empieza aquí primero
Esta guía amplía el proyecto del sitio de reservas con IA con notificaciones por email automáticas. Si todavía no has construido el sitio de reservas, empieza primero con la guía principal.
Construye un sitio de reservas con IA
Abre la guía principal
MAIN GUIDE
https://profitstudio.app/video/build-booking-website-ai
Configurar Resend para los emails de reserva
Crea y configura tu cuenta de Resend
Abrir Resend
https://resend.com/
Crea una cuenta de Resend Genera una clave API de Resend Copia y guarda la clave API de forma segura
IMPORTANTE
Mantén privada la clave API de Resend. Nunca la añadas al código del frontend ni la compartas públicamente.
Añade y verifica tu dominio de envío
Añade el dominio de tu sitio de reservas Selecciona la región más cercana Copia los registros DNS de Resend Añade los registros en tu proveedor de dominio Vuelve a Resend y confirma que el dominio está verificado
La verificación DNS puede tardar unos minutos o más, según tu proveedor de dominio.
Crear la función de email de reservas
Crea la Edge Function de correo de reservas
Abrir Supabase
https://supabase.com/?utm_source=partner&utm_medium=social&utm_campaign=supasquad&dub_id=faVfKdcLWvKCbYNp
Abre tu proyecto de Supabase Abre Edge Functions Crea una nueva función con el editor del navegador Nombra la función `send-booking-email` Elimina el código de ejemplo predeterminado
Personaliza y añade el código de la función
Introduce el correo del propietario Introduce el dominio verificado de Resend Introduce el nombre del negocio remitente Personaliza las plantillas de correo si es necesario Copia y pega el código de función generado
Función de email de reservas
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 del propietario
owner@example.com
Dominio verificado en Resend
yourdomain.com
Nombre del negocio remitente
Clínica Dental Bright
Asunto del email al propietario
Encabezado del email al propietario
Texto introductorio del email al propietario
Asunto del email al cliente
Encabezado del email al cliente
Texto introductorio del email al cliente
Texto de cierre del email al cliente
Despliega y configura la función
Despliega la función Abre la configuración de la función Desactiva `Verify JWT With Legacy Secret` Guarda los cambios Copia y guarda la URL del endpoint de la función para configurar el webhook
IMPORTANTE
Desactiva “Verify JWT With Legacy Secret” para que el Database Webhook pueda llamar a la función.
Añadir la API key de Resend a Supabase
Añade la clave API de Resend como secreto en Supabase
Abre Edge Function Secrets Crea un nuevo secreto Establece el nombre en `RESEND_API_KEY` Pega la clave API de Resend Guarda el secreto
IMPORTANTE
Usa exactamente el nombre de secreto `RESEND_API_KEY`. Cualquier error tipográfico impedirá que la función acceda a Resend.
Conectar los emails a las nuevas reservas
Crea el webhook de correo de reservas
Activa la función de correo de reserva cada vez que se añada una nueva cita.
Integrations Database Webhooks
Abre Database Webhooks Activa Database Webhooks Crea un nuevo webhook Nómbralo `send-booking-email-webhook` Selecciona la tabla `public.appointments` Elige `Insert` como evento Mantén `HTTP Request` como tipo Usa `POST` como método de solicitud Pega la URL del endpoint de la Edge Function Crea el webhook
Probar el sistema de email de reservas
Prueba el flujo completo de correos de reserva
Prueba el flujo completo de reserva y confirma que ambos correos se entregan correctamente.
Abre el sitio de reservas en vivo Envía una cita de prueba Confirma que la reserva se guarda en Supabase Revisa el correo de confirmación del cliente Revisa el correo de notificación del propietario Verifica que los datos de la reserva sean correctos en ambos correos Confirma que la reserva aparece en el panel de administración