import os import requests from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel app = FastAPI() # ---- Allow frontend access ---- app.add_middleware( CORSMiddleware, allow_origins=["https://voice-call-agent.vercel.app/"], # ✅ Restrict in production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ---- Environment variables ---- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") AIRTABLE_API_KEY = os.getenv("AIRTABLE_API_KEY") AIRTABLE_BASE_ID = os.getenv("AIRTABLE_BASE_ID") AIRTABLE_TABLE = "Appointments" # ---- Ephemeral Key Endpoint ---- @app.get("/get-ephemeral-key") def get_ephemeral_key(): """Generate an ephemeral client secret for OpenAI Realtime API""" url = "https://api.openai.com/v1/realtime/client_secrets" headers = { "Authorization": f"Bearer {OPENAI_API_KEY}", "Content-Type": "application/json", } body = { "session": { "type": "realtime", "model": "gpt-4o-mini-realtime-preview", "audio": { "output": {"voice": "verse"} }, } } res = requests.post(url, headers=headers, json=body) print("🔑 Ephemeral key request status:", res.status_code) data = res.json() print("🔑 Ephemeral key response:", data) return data # ---- Add Appointment ---- class Appointment(BaseModel): name: str phone: str date: str time: str service: str @app.post("/add") def add_appointment(appo: Appointment): """Add a new appointment to Airtable (auto-increment ID handled by Airtable)""" url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE}" headers = { "Authorization": f"Bearer {AIRTABLE_API_KEY}", "Content-Type": "application/json", } data = { "fields": { "Name": appo.name, "Phone": appo.phone, "Date": appo.date, "Time": appo.time, "Service": appo.service, } } r = requests.post(url, headers=headers, json=data) print("🔹 Airtable Add Response:", r.status_code, r.text) return r.json() # ---- Update Appointment ---- class UpdateAppointment(BaseModel): appointment_id: str name: str | None = None phone: str | None = None date: str | None = None time: str | None = None service: str | None = None @app.post("/update") def update_appointment(update: UpdateAppointment): """Update an existing appointment in Airtable using custom Appointment ID""" headers = { "Authorization": f"Bearer {AIRTABLE_API_KEY}", "Content-Type": "application/json", } # Step 1: Find record by Appointment ID field filter_formula = f"{{Appointment ID}} = '{update.appointment_id}'" search_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE}?filterByFormula={filter_formula}" search_res = requests.get(search_url, headers=headers).json() if not search_res.get("records"): print("❌ No record found for Appointment ID:", update.appointment_id) return {"status": "error", "message": "Appointment not found"} record_id = search_res["records"][0]["id"] # Step 2: Prepare updated fields fields = {} if update.name: fields["Name"] = update.name if update.phone: fields["Phone"] = update.phone if update.date: fields["Date"] = update.date if update.time: fields["Time"] = update.time if update.service: fields["Service"] = update.service # Step 3: Patch record by record ID update_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE}/{record_id}" patch_res = requests.patch(update_url, headers=headers, json={"fields": fields}) print("🔸 Airtable Update Response:", patch_res.status_code, patch_res.text) if patch_res.status_code == 200: return {"status": "updated"} else: return {"status": "error", "message": "Update failed"} class CancelAppointment(BaseModel): appointment_id: str @app.post("/cancel") def cancel_appointment(cancel: CancelAppointment): """Cancel appointment by custom Appointment ID field""" # Step 1: Search record by Appointment ID field filter_formula = f"{{Appointment ID}} = '{cancel.appointment_id}'" search_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE}?filterByFormula={filter_formula}" headers = {"Authorization": f"Bearer {AIRTABLE_API_KEY}"} search_res = requests.get(search_url, headers=headers).json() if not search_res.get("records"): print("❌ No record found for Appointment ID:", cancel.appointment_id) return {"status": "error", "message": "Appointment not found"} record_id = search_res["records"][0]["id"] print(f"✅ Found Record ID: {record_id} for Appointment ID: {cancel.appointment_id}") # Step 2: Delete record by Record ID delete_url = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE}/{record_id}" delete_res = requests.delete(delete_url, headers=headers) print("🗑️ Airtable Delete Response:", delete_res.status_code, delete_res.text) if delete_res.status_code == 200: return {"status": "deleted"} else: return {"status": "error", "message": "Delete failed"}