AI Randevu Web Sitenize SMS Bildirimleri Ekleyin
Biri randevu talebi gönderdiğinde otomatik SMS gönderin.
Yol Haritası ve Kaynaklar
Yeni misiniz? Önce Buradan Başlayın
Bu rehber, AI Randevu Web Sitesi projesini otomatik SMS bildirimleriyle yükseltir. Randevu web sitesini henüz oluşturmadıysanız, önce ana rehberle başlayın.
AI ile Randevu Web Sitesi Oluşturun
Ana rehberi açın
MAIN GUIDE
https://profitstudio.app/video/build-booking-website-ai
Twilio Bilgilerinizi Alın
Bir Twilio hesabı oluşturun
Twilio'yu açın
https://www.twilio.com/
Bu değerleri Twilio panelinizden alın:
Twilio Değerleri
TWILIO_ACCOUNT_SID TWILIO_AUTH_TOKEN TWILIO_PHONE_NUMBER
TWILIO_ACCOUNT_SID
Account SID
TWILIO_AUTH_TOKEN
Auth Token
TWILIO_PHONE_NUMBER
Twilio telefon numarası
SMS destekleyen bir Twilio numarası seçin
Supabase'de Secrets Ekleyin
Supabase Edge Function Secrets'ı açın
Supabase'i açın
https://supabase.com/dashboard/projects
Supabase → Project Settings → Edge Functions → Secrets
Gerekli Twilio secret'larını ekleyin
Gerekli Secret'lar
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=your_auth_token TWILIO_PHONE_NUMBER=+1234567890
Müşteriye SMS gönderimi varsayılan olarak açıktır. İşletme sahibine SMS gönderimi isteğe bağlıdır ve varsayılan olarak kapalıdır.
İsteğe bağlı: İşletme sahibine SMS gönder
İsteğe Bağlı İşletme Sahibi SMS'i
SEND_OWNER_SMS=true OWNER_PHONE_NUMBER=+1234567890
Bunları yalnızca yeni bir randevu talebi gönderildiğinde işletme sahibinin de SMS almasını istiyorsanız ekleyin.
SMS Edge Function'ını Oluşturun
Tam olarak bu adla yeni bir Edge Function oluşturun: send-sms-notification
SMS mesaj şablonunu gözden geçirin
Oluşturulan SMS fonksiyon kodunu kopyalayıp yapıştırın
SMS Bildirim Fonksiyonu
// supabase/functions/send-sms-notification/index.ts // // Sends SMS notifications when a new booking request is created. // Default: sends booking request SMS to the customer. // Optional: also sends SMS notification to the business owner. // // Required Supabase Secrets: // TWILIO_ACCOUNT_SID // TWILIO_AUTH_TOKEN // TWILIO_PHONE_NUMBER // // Optional Owner SMS Secrets: // SEND_OWNER_SMS=true // OWNER_PHONE_NUMBER=+1234567890 const CORS_HEADERS = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type", }; // ===== CUSTOMIZE THESE VALUES ===== const BUSINESS_NAME = "Your Business Name"; const CUSTOMER_SMS_TEMPLATE = "Hi {{customer_name}}, we received your {{service_name}} booking request for {{appointment_date}} at {{appointment_time}}. We'll contact you soon to confirm."; const OWNER_SMS_TEMPLATE = "New booking request: {{customer_name}} requested {{service_name}} on {{appointment_date}} at {{appointment_time}}. Phone: {{customer_phone}}"; // Default booking field names used in this guide. // If your project uses different field names, update them here. const FIELD_MAP = { customerName: ["full_name", "customer_name", "name"], customerPhone: ["phone", "phone_number", "customer_phone"], appointmentDate: ["appointment_date", "date", "booking_date"], appointmentTime: ["start_time", "appointment_time", "time"], serviceName: ["service_name", "service"], serviceId: ["service_id"], }; // ===== END CUSTOMIZATION ===== function json(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { ...CORS_HEADERS, "Content-Type": "application/json" }, }); } function getEnvFlag(name: string, defaultValue = false): boolean { const value = Deno.env.get(name); if (!value) return defaultValue; return value.toLowerCase() === "true"; } function getField(record: Record<string, unknown>, keys: string[]): string { for (const key of keys) { const value = record[key]; if (typeof value === "string" && value.trim()) { return value.trim(); } } return ""; } function formatTime(time: string): string { if (!time) return ""; return time.length >= 5 ? time.slice(0, 5) : time; } function renderTemplate( template: string, values: Record<string, string>, ): string { return template.replace(/\{\{(.*?)\}\}/g, (_, key) => { return values[key.trim()] ?? ""; }); } async function fetchServiceName( serviceId: string, supabaseUrl: string, serviceRoleKey: string, ): Promise<string> { try { const url = `${supabaseUrl}/rest/v1/services?id=eq.${encodeURIComponent( serviceId, )}&select=name`; const response = await fetch(url, { headers: { apikey: serviceRoleKey, Authorization: `Bearer ${serviceRoleKey}`, }, }); if (!response.ok) return ""; const rows = await response.json(); return rows?.[0]?.name ?? ""; } catch { return ""; } } async function sendTwilioSms(options: { accountSid: string; authToken: string; from: string; to: string; body: string; }) { const { accountSid, authToken, from, to, body } = options; const url = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json`; const form = new URLSearchParams({ From: from, To: to, Body: body, }); const response = await fetch(url, { method: "POST", headers: { Authorization: "Basic " + btoa(`${accountSid}:${authToken}`), "Content-Type": "application/x-www-form-urlencoded", }, body: form.toString(), }); const data = await response.json().catch(() => null); return { ok: response.ok, status: response.status, data, }; } Deno.serve(async (req) => { if (req.method === "OPTIONS") { return new Response("ok", { headers: CORS_HEADERS }); } if (req.method !== "POST") { return json({ success: false, error: "Method not allowed" }, 405); } const accountSid = Deno.env.get("TWILIO_ACCOUNT_SID"); const authToken = Deno.env.get("TWILIO_AUTH_TOKEN"); const twilioPhoneNumber = Deno.env.get("TWILIO_PHONE_NUMBER"); const sendCustomerSms = getEnvFlag("SEND_CUSTOMER_SMS", true); const sendOwnerSms = getEnvFlag("SEND_OWNER_SMS", false); const ownerPhoneNumber = Deno.env.get("OWNER_PHONE_NUMBER"); if (!accountSid || !authToken || !twilioPhoneNumber) { return json( { success: false, error: "Missing Twilio secrets. Please add TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER.", }, 500, ); } let payload; try { payload = await req.json(); } catch { return json({ success: false, error: "Invalid JSON body" }, 400); } const record = payload?.record; if (!record) { return json({ success: false, error: "Missing record in payload" }, 400); } const customerName = getField(record, FIELD_MAP.customerName) || "Customer"; const customerPhone = getField(record, FIELD_MAP.customerPhone); const appointmentDate = getField(record, FIELD_MAP.appointmentDate) || "your selected date"; const appointmentTime = formatTime(getField(record, FIELD_MAP.appointmentTime)) || "your selected time"; let serviceName = getField(record, FIELD_MAP.serviceName) || "your service"; const serviceId = getField(record, FIELD_MAP.serviceId); const supabaseUrl = Deno.env.get("SUPABASE_URL"); const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"); if (serviceId && supabaseUrl && serviceRoleKey) { const fetchedServiceName = await fetchServiceName( serviceId, supabaseUrl, serviceRoleKey, ); if (fetchedServiceName) { serviceName = fetchedServiceName; } } const templateValues = { business_name: BUSINESS_NAME, customer_name: customerName, customer_phone: customerPhone || "Not provided", service_name: serviceName, appointment_date: appointmentDate, appointment_time: appointmentTime, }; const results = { customer_sms: "not_sent", owner_sms: "not_sent", errors: [] as unknown[], }; if (sendCustomerSms) { if (!customerPhone) { results.customer_sms = "skipped_missing_customer_phone"; } else { const customerMessage = renderTemplate( CUSTOMER_SMS_TEMPLATE, templateValues, ); const customerResult = await sendTwilioSms({ accountSid, authToken, from: twilioPhoneNumber, to: customerPhone, body: customerMessage, }); if (!customerResult.ok) { results.customer_sms = "failed"; results.errors.push({ type: "customer_sms_failed", status: customerResult.status, twilio: customerResult.data, }); } else { results.customer_sms = "sent"; } } } if (sendOwnerSms) { if (!ownerPhoneNumber) { results.owner_sms = "skipped_missing_owner_phone"; } else { const ownerMessage = renderTemplate( OWNER_SMS_TEMPLATE, templateValues, ); const ownerResult = await sendTwilioSms({ accountSid, authToken, from: twilioPhoneNumber, to: ownerPhoneNumber, body: ownerMessage, }); if (!ownerResult.ok) { results.owner_sms = "failed"; results.errors.push({ type: "owner_sms_failed", status: ownerResult.status, twilio: ownerResult.data, }); } else { results.owner_sms = "sent"; } } } if (results.errors.length > 0) { return json( { success: false, message: "One or more SMS messages failed.", results, }, 502, ); } return json({ success: true, message: "SMS notification process completed.", results, }); });
İşletme Adı
İşletme Adınız
Müşteri SMS Mesajı
Merhaba {{customer_name}}, {{appointment_date}} tarihinde saat {{appointment_time}} için {{service_name}} randevu talebinizi aldık. Onaylamak için kısa süre içinde sizinle iletişime geçeceğiz.
İşletme Sahibi SMS Mesajı
Yeni randevu talebi: {{customer_name}}, {{appointment_date}} tarihinde saat {{appointment_time}} için {{service_name}} talep etti. Telefon: {{customer_phone}}
İsteğe bağlı: gerekirse alan adlarını güncelleyin
Bu SMS fonksiyonu şu yaygın randevu alanlarıyla çalışır: full_name, phone, appointment_date, start_time, service_id ve service_name. Projeniz farklı alan adları kullanıyorsa, randevu tablonuzla eşleşmesi için fonksiyon içindeki FIELD_MAP değerini güncelleyin.
FIELD_MAP
const FIELD_MAP = { customerName: ["full_name", "customer_name", "name"], customerPhone: ["phone", "phone_number", "customer_phone"], appointmentDate: ["appointment_date", "date", "booking_date"], appointmentTime: ["start_time", "appointment_time", "time"], serviceName: ["service_name", "service"], serviceId: ["service_id"], };
Webhook'u Bağlayın
Randevu tablonuzu seçin
Bu videoda şunu kullanıyoruz: public.appointments Projeniz farklı bir randevu tablosu kullanıyorsa onun yerine o tabloyu seçin.
Supabase Database Webhooks bölümüne gidin
Supabase → Database → Webhooks → Create webhook
Bu webhook ayarlarını kullanın
Webhook Ayarları
Table: public.appointments Event: Insert Type: Supabase Edge Functions Function: send-sms-notification Method: POST
Yalnızca Insert olayını etkinleştirin
SMS Bildirimini Test Edin
Web sitenizden yeni bir randevu oluşturun
Müşterinin SMS'i alıp almadığını kontrol edin
Supabase Edge Function Loglarını Kontrol Edin
Supabase → Edge Functions → send-sms-notification → Logs
Gerekirse Twilio Messaging Loglarını kontrol edin
Twilio → Messaging → Logs
Yayına almadan önce ülkenizin SMS kurallarını kontrol edin
SMS gereklilikleri ülkeye göre değişir
SMS gereklilikleri hedef ülkeye göre değişebilir. Bunu canlı ortamda kullanmadan önce hedef ülkeniz için Twilio'nun mesajlaşma gerekliliklerini kontrol edin. Örneğin, ABD numaralarına uygulama üzerinden SMS gönderimi A2P 10DLC kaydı gerektirebilir.