अपनी AI बुकिंग वेबसाइट में SMS नोटिफिकेशन जोड़ें

जब कोई बुकिंग अनुरोध सबमिट करे तो स्वचालित SMS भेजें।

रोडमैप और संसाधन

यहाँ नए हैं? पहले यहाँ से शुरू करें

यह गाइड AI बुकिंग वेबसाइट प्रोजेक्ट को स्वचालित SMS नोटिफिकेशन के साथ अपग्रेड करता है। यदि आपने अभी तक बुकिंग वेबसाइट नहीं बनाई है, तो पहले मुख्य गाइड से शुरू करें।

AI के साथ एक बुकिंग वेबसाइट बनाएँ

मुख्य गाइड खोलें

MAIN GUIDE

https://profitstudio.app/video/build-booking-website-ai

अपनी Twilio जानकारी प्राप्त करें

Twilio खाता बनाएँ

Twilio खोलें

https://www.twilio.com/

ये मान अपने Twilio डैशबोर्ड से प्राप्त करें:

Twilio मान

TWILIO_ACCOUNT_SID TWILIO_AUTH_TOKEN TWILIO_PHONE_NUMBER

TWILIO_ACCOUNT_SID

Account SID

TWILIO_AUTH_TOKEN

Auth Token

TWILIO_PHONE_NUMBER

Twilio फ़ोन नंबर

एक Twilio नंबर चुनें जो SMS का समर्थन करता हो

Supabase में सीक्रेट जोड़ें

Supabase Edge Function सीक्रेट खोलें

Supabase खोलें

https://supabase.com/dashboard/projects

Supabase → Project Settings → Edge Functions → Secrets

आवश्यक Twilio सीक्रेट जोड़ें

आवश्यक सीक्रेट

TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=your_auth_token TWILIO_PHONE_NUMBER=+1234567890

ग्राहक SMS डिफ़ॉल्ट रूप से चालू है। मालिक SMS वैकल्पिक है और डिफ़ॉल्ट रूप से बंद है।

वैकल्पिक: व्यवसाय के मालिक को SMS भेजें

वैकल्पिक मालिक SMS

SEND_OWNER_SMS=true OWNER_PHONE_NUMBER=+1234567890

इन्हें केवल तभी जोड़ें जब आप चाहते हैं कि नया बुकिंग अनुरोध सबमिट होने पर व्यवसाय के मालिक को भी SMS मिले।

SMS Edge Function बनाएँ

इस सटीक नाम के साथ एक नई Edge Function बनाएँ: send-sms-notification

SMS संदेश टेम्प्लेट की समीक्षा करें

जेनरेट किया गया SMS फ़ंक्शन कोड कॉपी करके पेस्ट करें

SMS नोटिफिकेशन फ़ंक्शन

// 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, }); });

व्यवसाय का नाम

आपका व्यवसाय का नाम

ग्राहक SMS संदेश

नमस्ते {{customer_name}}, हमें {{appointment_date}} को {{appointment_time}} बजे के लिए आपका {{service_name}} बुकिंग अनुरोध मिल गया है। पुष्टि के लिए हम जल्द ही आपसे संपर्क करेंगे।

मालिक SMS संदेश

नया बुकिंग अनुरोध: {{customer_name}} ने {{appointment_date}} को {{appointment_time}} पर {{service_name}} का अनुरोध किया। फ़ोन: {{customer_phone}}

वैकल्पिक: ज़रूरत हो तो फ़ील्ड के नाम अपडेट करें

यह SMS फ़ंक्शन सामान्य बुकिंग फ़ील्ड्स के साथ काम करता है, जैसे: full_name, phone, appointment_date, start_time, service_id, और service_name। यदि आपका प्रोजेक्ट अलग फ़ील्ड नामों का उपयोग करता है, तो अपनी बुकिंग टेबल से मेल खाने के लिए फ़ंक्शन में FIELD_MAP को अपडेट करें।

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 कनेक्ट करें

अपनी बुकिंग टेबल चुनें

इस वीडियो में हम उपयोग करते हैं: public.appointments यदि आपका प्रोजेक्ट किसी अन्य बुकिंग टेबल का उपयोग करता है, तो उस टेबल को चुनें।

Supabase Database Webhooks पर जाएँ

Supabase → Database → Webhooks → Create webhook

इन webhook सेटिंग्स का उपयोग करें

Webhook सेटिंग्स

Table: public.appointments Event: Insert Type: Supabase Edge Functions Function: send-sms-notification Method: POST

केवल Insert इवेंट सक्षम करें

SMS नोटिफिकेशन का परीक्षण करें

अपनी वेबसाइट से एक नई बुकिंग बनाएँ

जाँचें कि क्या ग्राहक को SMS मिला

Supabase Edge Function लॉग देखें

Supabase → Edge Functions → send-sms-notification → Logs

ज़रूरत होने पर Twilio Messaging लॉग देखें

Twilio → Messaging → Logs

लाइव होने से पहले, अपने देश के SMS नियम जाँचें

SMS आवश्यकताएँ देश के अनुसार अलग-अलग होती हैं

SMS आवश्यकताएँ गंतव्य देश के अनुसार अलग-अलग हो सकती हैं। इसे प्रोडक्शन में उपयोग करने से पहले, अपने लक्षित देश के लिए Twilio की मैसेजिंग आवश्यकताएँ जाँचें। उदाहरण के लिए, U.S. नंबरों पर ऐप-आधारित SMS के लिए A2P 10DLC पंजीकरण आवश्यक हो सकता है।