Adicione notificações por e-mail ao seu site de agendamento com IA
Envie e-mails de confirmação de agendamento e alertas ao dono usando Supabase, Resend e Edge Functions.
Roadmap e recursos
É novo por aqui? Comece por aqui primeiro
Este guia atualiza o projeto do site de agendamento com IA com notificações automáticas por e-mail. Se você ainda não construiu o site de agendamento, comece primeiro pelo guia principal.
Crie um site de agendamento com IA
Abra o guia principal
MAIN GUIDE
https://profitstudio.app/video/build-booking-website-ai
Configurar o Resend para e-mails de agendamento
Crie e configure a sua conta Resend
Abrir o Resend
https://resend.com/
Crie uma conta Resend Gere uma chave API do Resend Copie e guarde a chave API em segurança
IMPORTANTE
Mantenha a chave API do Resend privada. Nunca a adicione ao código do frontend nem a partilhe publicamente.
Adicione e verifique o seu domínio de envio
Adicione o domínio do seu site de reservas Selecione a região mais próxima Copie os registos DNS do Resend Adicione os registos no seu fornecedor de domínio Volte ao Resend e confirme que o domínio está verificado
A verificação DNS pode demorar alguns minutos ou mais, consoante o seu fornecedor de domínio.
Criar a função de e-mail de agendamento
Crie a Edge Function de e-mail de reservas
Abrir o Supabase
https://supabase.com/?utm_source=partner&utm_medium=social&utm_campaign=supasquad&dub_id=faVfKdcLWvKCbYNp
Abra o seu projeto Supabase Abra as Edge Functions Crie uma nova função com o editor do navegador Dê à função o nome `send-booking-email` Remova o código inicial predefinido
Personalize e adicione o código da função
Introduza o e-mail do proprietário Introduza o domínio Resend verificado Introduza o nome do negócio remetente Personalize os modelos de e-mail se necessário Copie e cole o código da função gerado
Função de e-mail de agendamento
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" }, } ); } });
E-mail do dono
owner@example.com
Domínio verificado no Resend
yourdomain.com
Nome do negócio remetente
Clínica Dentária Bright
Assunto do e-mail ao dono
Cabeçalho do e-mail ao dono
Texto de introdução do e-mail ao dono
Assunto do e-mail ao cliente
Cabeçalho do e-mail ao cliente
Texto de introdução do e-mail ao cliente
Texto de encerramento do e-mail ao cliente
Faça o deploy e configure a função
Faça o deploy da função Abra as definições da função Desative `Verify JWT With Legacy Secret` Guarde as alterações Copie e guarde o URL do endpoint da função para configurar o webhook
IMPORTANTE
Desative “Verify JWT With Legacy Secret” para que o Database Webhook possa chamar a função.
Adicionar a API key do Resend ao Supabase
Adicione a chave API do Resend como segredo no Supabase
Abra os Edge Function Secrets Crie um novo segredo Defina o nome como `RESEND_API_KEY` Cole a chave API do Resend Guarde o segredo
IMPORTANTE
Use exatamente o nome de segredo `RESEND_API_KEY`. Qualquer erro de escrita impedirá a função de aceder ao Resend.
Conectar e-mails a novos agendamentos
Crie o webhook de e-mail de reservas
Acione a função de e-mail de reserva sempre que uma nova marcação for adicionada.
Integrations Database Webhooks
Abra os Database Webhooks Ative os Database Webhooks Crie um novo webhook Dê-lhe o nome `send-booking-email-webhook` Selecione a tabela `public.appointments` Escolha `Insert` como evento Mantenha `HTTP Request` como tipo Use `POST` como método do pedido Cole o URL do endpoint da Edge Function Crie o webhook
Testar o sistema de e-mail de agendamento
Teste o fluxo completo de e-mails de reserva
Teste o fluxo completo de reserva e confirme que ambos os e-mails são entregues corretamente.
Abra o site de reservas em direto Envie uma marcação de teste Confirme que a reserva foi guardada no Supabase Verifique o e-mail de confirmação do cliente Verifique o e-mail de notificação do proprietário Confirme que os dados da reserva estão corretos em ambos os e-mails Confirme que a reserva aparece no painel de administração