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.