Prompt System
The AI receptionist's system prompt is built from a Firestore template + business data. This doc explains the full pipeline.
Pipeline
Firestore template Business document Session context
(config/prompts/templates) (businesses/{uid}) (channel, callerPhone, etc.)
│ │ │
└──────────┬───────────────┘──────────────────────────┘
▼
buildPromptContext() ← prompt.js: builds context object from biz data
│
▼
renderPrompt() ← promptService.js: fetches template, interpolates {{vars}}
│
▼
Final system prompt → sent to OpenAI Realtime / Chat CompletionsTemplate Source
Templates live in Firestore at config/prompts/templates/{templateId}. The main template is receptionist.
Templates are seeded from docs/prompts/receptionist.md via scripts/seed-prompts.mjs. Editing the markdown file alone does nothing — you must re-run the seed script to push changes to Firestore.
The admin UI (AdminPrompts.tsx) can also edit templates directly in Firestore.
There is no fallback. If the Firestore template is missing, the API throws. Seed with:
GOOGLE_APPLICATION_CREDENTIALS=path/to/key.json node scripts/seed-prompts.mjsTemplate Variables
The template uses placeholders. The buildPromptContext() function in prompt.js produces the context object:
| Variable | Source | Notes |
|---|---|---|
name | ai.aiName | Falls back to "the receptionist" |
businessName | profile.name | Falls back to vertical terminology |
tone | ai.tone | |
greeting | ai.greeting | |
triageRules | Vertical config | Industry-specific safety rules (e.g. medical triage) |
calendarInstructions | Built conditionally | Only if Google Calendar is connected. Includes verification flow instructions that vary by ai.callerVerification level and channel (phone vs web). |
contactInfo | profile.phone/fax/email/website/address | |
locations | profile.locations[] | |
practitioners | profile.practitioners[] | Includes qualifications, specialties, bio |
hours | profile.hours[] | Open days only |
firstVisitInfo | profile.firstVisitInfo[] | |
bookingRequirements | profile.booking | Referral, required fields, instructions |
fees | profile.fees | Consultation fees, payment methods, Medicare info |
faqs | knowledge[] | Q&A pairs |
customPrompt | ai.customPrompt | Free-form instructions appended to prompt |
escalation | escalation[] | Generates tool-use instructions per escalation rule |
callControl | Conditional | Phone-only. Rules for handling silence, filler sounds, goodbye, abusive callers. |
sessionInfo | Conditional | Caller phone number (from Twilio) and return caller name if recognized. |
Unused in the template resolve to empty string (not an error).
Two Prompt Endpoints
GET /api/prompt — used by the browser voice client (VoiceGPT.tsx)
- Auth: business owner's Firebase ID token
- Business ID: from token's
uidclaim - No session context (no caller phone, no channel flag)
GET /api/portal/prompt — used by the portal chat (ChatGPT.tsx)
- Auth: any authenticated user (visitor or owner)
- Business ID: from
?businessId=query param - Side effects: sets
visitorcustom claim if user has no role, writes tovisitors/{uid}/businesses/{bizId}
Caching
promptService.js caches Firestore reads (templates and tool docs) in memory with a 60-second TTL. Changes to templates take up to 60s to take effect on a running API instance.