Comprehensive guide to deploying voice agents in educational institutions for automating admission inquiries, fee collection, attendance tracking, parent communication, and result announcements.
Use Cases#
| Use Case | Type | Description |
|---|---|---|
| Admission Inquiries | Inbound | Answer questions about courses, eligibility, fees, and admission process |
| Fee Reminders | Outbound | Automated calls for fee payment reminders and confirmations |
| Attendance Notifications | Outbound | Alert parents about student absence or late arrivals |
| Parent Communication | Both | General updates, PTM reminders, event notifications |
| Exam Results | Outbound | Announce results with grades and performance summary |
| Transport Updates | Outbound | Bus delay notifications, route changes |
Agent Configuration#
Admission Inquiry Agent#
{
"agent": {
"name": "Campus Admissions",
"language": "en-IN",
"llmProvider": "gemini-2.5",
"llmModel": "gemini-2.5-flash-lite",
"llmTemperature": 0.6,
"sttProvider": "deepgram",
"sttModel": "nova-3",
"sttConfig": {
"endpointing": 350,
"interimResults": true,
"keywords": ["admission:2", "fees:2", "scholarship:2", "hostel:2"]
},
"ttsProvider": "azure",
"ttsVoice": "en-IN-NeerjaNeural",
"ttsConfig": {
"rate": "0%",
"pitch": "0%"
},
"greetingMessage": "Thank you for calling ABC School admissions. I'm here to help you with information about our programs, admission process, and fees. How may I assist you today?",
"allowInterruptions": true,
"prompt": "...",
"tools": [],
"webhooks": {
"url": "https://your-server.com/webhooks/education",
"events": ["call.ended", "call.analyzed"]
}
}
}
Fee Reminder Agent (Outbound)#
{
"agent": {
"name": "Fee Reminder",
"language": "hi-IN",
"type": "outbound",
"llmProvider": "gemini-2.5",
"llmModel": "gemini-2.5-flash-lite",
"llmTemperature": 0.5,
"sttProvider": "deepgram",
"sttModel": "nova-3",
"sttConfig": {
"language": "hi",
"endpointing": 400
},
"ttsProvider": "azure",
"ttsVoice": "hi-IN-SwaraNeural",
"greetingMessage": "Namaste {{parentName}} ji. Main ABC School se bol raha/rahi hoon. {{studentName}} ki {{feeType}} fees ke baare mein baat karni thi.",
"prompt": "...",
"variables": {
"parentName": "Parent Name",
"studentName": "Student Name",
"studentClass": "Class/Grade",
"feeType": "Fee Type",
"feeAmount": "Amount Due",
"dueDate": "Due Date",
"paymentLink": "Payment URL"
}
}
}
System Prompts#
Admission Inquiry Agent#
You are a friendly and knowledgeable admissions counselor for {{institutionName}}, a {{institutionType}}.
## Your Role
- Answer questions about courses, programs, and eligibility criteria
- Explain the admission process and required documents
- Provide fee structure and scholarship information
- Schedule campus visits and counseling sessions
- Collect prospective student information for follow-up
## Institution Information
- Name: {{institutionName}}
- Type: {{institutionType}} (School/College/University)
- Courses: {{coursesList}}
- Admission Open: {{admissionPeriod}}
- Website: {{websiteUrl}}
## Response Guidelines
### Style
- Be warm, professional, and encouraging
- Keep responses concise (2-3 sentences max)
- Use simple language, avoid jargon
- Be patient with parents who may not speak English fluently
### Information Flow
1. Greet and understand their query
2. Identify the program/class they're interested in
3. Provide relevant information
4. Collect contact details for follow-up
5. Offer to schedule a campus visit
### Common Questions
**Admission Process:**
"Our admission process has three steps: First, you submit an online application with documents. Then, there's an entrance test on {{testDate}}. Finally, we conduct a personal interview."
**Fees:**
"For {{className}}, the annual fee is {{feeAmount}}. This includes tuition, books, and uniform. We also offer EMI payment options and scholarships for meritorious students."
**Eligibility:**
"For {{programName}}, you need {{eligibilityCriteria}}. The minimum marks requirement is {{minMarks}}%."
## Scholarship Information
Mention these scholarships when relevant:
- Merit Scholarship: 25% off for 90%+ in previous class
- Sibling Discount: 10% for second child
- EWS Quota: As per government norms
## What NOT to Do
- Don't confirm admission without proper process
- Don't commit to specific scholarship amounts
- Don't share other students' information
- Don't criticize other institutions
Fee Reminder Agent (Hindi)#
Aap {{institutionName}} ke fee reminder assistant hain.
## Aapka Kaam
- Parents ko fees ki yaad dilaana
- Payment options batana
- Payment confirmation lena
- Zaroorat ho toh installment plan discuss karna
## Call Flow
### 1. Introduction
"Namaste {{parentName}} ji. Main {{institutionName}} se bol raha/rahi hoon. {{studentName}}, class {{studentClass}} ki {{feeType}} fees ke baare mein baat karni thi."
### 2. Fee Details
"Aapki pending fees ₹{{feeAmount}} hai, jiska due date {{dueDate}} hai."
### 3. Payment Options
"Aap online payment kar sakte hain ya school office mein bhi payment kar sakte hain. Kya aapko payment link SMS kar doon?"
### 4. If Payment Difficulty
"Main samajh sakta/sakti hoon. Hum installment option bhi de sakte hain. Kya aap accountant se baat karna chahenge?"
## Guidelines
- Samman se baat karein (aap/ji ka istemaal)
- Pressure na dein, samjhdaari se baat karein
- Payment link bhejne ki offer karein
- Zaroorat ho toh human agent ko transfer karein
## Handling Common Responses
"Payment kar diya hai":
→ "Dhanyavaad! Kya aap mujhe transaction number bata sakte hain taaki main verify kar sakoon?"
"Abhi nahi kar sakta":
→ "Koi baat nahi. Kya aap mujhe bata sakte hain kab tak payment ho paayega?"
"Installment chahiye":
→ "Bilkul. Main aapko accountant se connect kar deta/deti hoon jo aapko best plan bata sakenge."
Attendance Notification Agent#
You are an automated attendance notification system for {{institutionName}}.
## Purpose
Inform parents when their child is absent or arrives late.
## Call Script
### Absent Notification
"Good morning {{parentName}}. This is an automated call from {{institutionName}}. We noticed that {{studentName}} from class {{studentClass}} is not present in school today, {{currentDate}}. If this is planned, no action is needed. Otherwise, please contact the school office. Thank you."
### Late Arrival
"Good morning {{parentName}}. This is {{institutionName}} calling. {{studentName}} arrived at school late today at {{arrivalTime}}. This is for your information. Thank you."
## Guidelines
- Keep the message brief and factual
- Don't alarm parents unnecessarily
- Provide school contact number for questions
- End call politely if no response needed
Tools#
Get Student Information#
{
"type": "function",
"function": {
"name": "get_student_info",
"description": "Retrieve student details including class, section, fee status, and attendance",
"parameters": {
"type": "object",
"properties": {
"student_id": {
"type": "string",
"description": "Student ID or enrollment number"
},
"parent_phone": {
"type": "string",
"description": "Parent's registered phone number"
}
},
"required": []
}
}
}
Check Fee Status#
{
"type": "function",
"function": {
"name": "check_fee_status",
"description": "Get pending fees and payment history for a student",
"parameters": {
"type": "object",
"properties": {
"student_id": {
"type": "string",
"description": "Student ID"
},
"fee_type": {
"type": "string",
"enum": ["tuition", "transport", "hostel", "examination", "all"],
"description": "Type of fee to check"
}
},
"required": ["student_id"]
}
}
}
Schedule Campus Visit#
{
"type": "function",
"function": {
"name": "schedule_campus_visit",
"description": "Book a campus visit or counseling session for prospective students",
"parameters": {
"type": "object",
"properties": {
"parent_name": {
"type": "string",
"description": "Parent's name"
},
"phone": {
"type": "string",
"description": "Contact phone number"
},
"student_name": {
"type": "string",
"description": "Prospective student's name"
},
"current_class": {
"type": "string",
"description": "Student's current class"
},
"interested_class": {
"type": "string",
"description": "Class seeking admission for"
},
"preferred_date": {
"type": "string",
"description": "Preferred visit date"
},
"preferred_time": {
"type": "string",
"enum": ["morning", "afternoon"],
"description": "Preferred time slot"
}
},
"required": ["parent_name", "phone", "interested_class"]
}
}
}
Send Payment Link#
{
"type": "function",
"function": {
"name": "send_payment_link",
"description": "Send fee payment link via SMS to parent",
"parameters": {
"type": "object",
"properties": {
"student_id": {
"type": "string",
"description": "Student ID"
},
"phone": {
"type": "string",
"description": "Phone number to send SMS"
},
"amount": {
"type": "number",
"description": "Payment amount"
},
"fee_type": {
"type": "string",
"description": "Type of fee"
}
},
"required": ["student_id", "phone", "amount"]
}
}
}
Record Payment Promise#
{
"type": "function",
"function": {
"name": "record_payment_promise",
"description": "Record when parent promises to pay by a certain date",
"parameters": {
"type": "object",
"properties": {
"student_id": {
"type": "string"
},
"promised_date": {
"type": "string",
"description": "Date parent promised to pay (YYYY-MM-DD)"
},
"promised_amount": {
"type": "number",
"description": "Amount promised"
},
"notes": {
"type": "string",
"description": "Any additional notes"
}
},
"required": ["student_id", "promised_date"]
}
}
}
Transfer to Counselor#
{
"type": "function",
"function": {
"name": "transfer_to_counselor",
"description": "Transfer call to human admission counselor or accountant",
"parameters": {
"type": "object",
"properties": {
"department": {
"type": "string",
"enum": ["admissions", "accounts", "principal", "transport"],
"description": "Department to transfer to"
},
"reason": {
"type": "string",
"description": "Reason for transfer"
},
"priority": {
"type": "string",
"enum": ["normal", "urgent"],
"description": "Call priority"
}
},
"required": ["department", "reason"]
}
}
}
Tool Handlers#
package education
import (
"context"
"fmt"
"time"
)
type StudentInfo struct {
ID string
Name string
Class string
Section string
ParentName string
ParentPhone string
FeeStatus FeeStatus
Attendance float64
}
type FeeStatus struct {
TotalDue float64
PendingAmount float64
DueDate time.Time
LastPayment time.Time
FeeBreakdown map[string]float64
}
func handleGetStudentInfo(ctx context.Context, params map[string]any) (any, error) {
var student *StudentInfo
var err error
if studentID, ok := params["student_id"].(string); ok {
student, err = studentService.GetByID(studentID)
} else if phone, ok := params["parent_phone"].(string); ok {
student, err = studentService.GetByParentPhone(phone)
}
if err != nil {
return map[string]any{
"found": false,
"message": "I couldn't find the student record. Could you verify the details?",
}, nil
}
return map[string]any{
"found": true,
"name": student.Name,
"class": student.Class,
"section": student.Section,
"parent_name": student.ParentName,
"fee_pending": student.FeeStatus.PendingAmount,
"attendance": fmt.Sprintf("%.1f%%", student.Attendance),
}, nil
}
func handleCheckFeeStatus(ctx context.Context, params map[string]any) (any, error) {
studentID := params["student_id"].(string)
feeType := "all"
if ft, ok := params["fee_type"].(string); ok {
feeType = ft
}
fees, err := feeService.GetPendingFees(studentID, feeType)
if err != nil {
return nil, err
}
if fees.PendingAmount == 0 {
return map[string]any{
"status": "paid",
"message": "All fees are paid. Thank you!",
}, nil
}
return map[string]any{
"status": "pending",
"amount": fees.PendingAmount,
"due_date": fees.DueDate.Format("2 January 2006"),
"breakdown": fees.Breakdown,
"message": fmt.Sprintf("Pending amount is Rs. %.0f, due by %s", fees.PendingAmount, fees.DueDate.Format("2 January")),
}, nil
}
func handleScheduleCampusVisit(ctx context.Context, params map[string]any) (any, error) {
visit := CampusVisit{
ParentName: params["parent_name"].(string),
Phone: params["phone"].(string),
InterestedClass: params["interested_class"].(string),
}
if name, ok := params["student_name"].(string); ok {
visit.StudentName = name
}
// Get next available slot
slot, err := calendarService.GetNextAvailableSlot(params["preferred_date"], params["preferred_time"])
if err != nil {
return map[string]any{
"booked": false,
"message": "I couldn't find an available slot. Let me connect you with our admissions team.",
}, nil
}
// Book the visit
booking, err := visitService.Book(visit, slot)
if err != nil {
return nil, err
}
return map[string]any{
"booked": true,
"date": slot.Date.Format("Monday, 2 January"),
"time": slot.Time,
"confirmation": booking.ConfirmationID,
"message": fmt.Sprintf("Your campus visit is scheduled for %s at %s. You'll receive an SMS confirmation shortly.",
slot.Date.Format("2 January"), slot.Time),
}, nil
}
func handleSendPaymentLink(ctx context.Context, params map[string]any) (any, error) {
studentID := params["student_id"].(string)
phone := params["phone"].(string)
amount := params["amount"].(float64)
// Generate payment link
link, err := paymentService.GenerateLink(studentID, amount)
if err != nil {
return nil, err
}
// Send SMS
err = smsService.Send(phone, fmt.Sprintf(
"Pay school fees of Rs. %.0f for your ward. Click here: %s. Valid for 24 hours.",
amount, link.ShortURL,
))
if err != nil {
return map[string]any{
"sent": false,
"message": "I couldn't send the SMS right now. Let me try again.",
}, nil
}
return map[string]any{
"sent": true,
"message": "I've sent the payment link to your registered mobile number. It's valid for 24 hours.",
}, nil
}
Multi-Language Support#
Educational institutions in India need to communicate with parents in their preferred language.
Language Configuration by Region#
var RegionalLanguageConfig = map[string]AgentConfig{
"north_india": {
Languages: []string{"hi-IN", "en-IN", "pa-IN"},
DefaultLang: "hi-IN",
STT: STTConfig{
Provider: "deepgram",
Model: "nova-3",
},
TTS: TTSConfig{
Provider: "azure",
Voices: map[string]string{
"hi-IN": "hi-IN-SwaraNeural",
"en-IN": "en-IN-NeerjaNeural",
"pa-IN": "pa-IN-GurpreetNeural",
},
},
},
"south_india": {
Languages: []string{"ta-IN", "te-IN", "kn-IN", "ml-IN", "en-IN"},
DefaultLang: "en-IN",
STT: STTConfig{
Provider: "google",
Model: "chirp_2",
},
TTS: TTSConfig{
Provider: "azure",
Voices: map[string]string{
"ta-IN": "ta-IN-PallaviNeural",
"te-IN": "te-IN-ShrutiNeural",
"kn-IN": "kn-IN-SapnaNeural",
"ml-IN": "ml-IN-SobhanaNeural",
"en-IN": "en-IN-NeerjaNeural",
},
},
},
"east_india": {
Languages: []string{"bn-IN", "as-IN", "or-IN", "en-IN"},
DefaultLang: "bn-IN",
STT: STTConfig{
Provider: "elevenlabs",
Model: "scribe",
},
TTS: TTSConfig{
Provider: "azure",
Voices: map[string]string{
"bn-IN": "bn-IN-TanishaaNeural",
"as-IN": "as-IN-YashicaNeural",
"or-IN": "or-IN-SubhasiniNeural",
"en-IN": "en-IN-NeerjaNeural",
},
},
},
}
Language Selection by Parent Preference#
{
"agent": {
"name": "Multilingual Fee Reminder",
"defaultLanguage": "hi-IN",
"supportedLanguages": ["hi-IN", "en-IN", "ta-IN", "te-IN"],
"greetingMessage": {
"hi-IN": "Namaste {{parentName}} ji. Main {{schoolName}} se bol raha hoon.",
"en-IN": "Hello {{parentName}}. This is a call from {{schoolName}}.",
"ta-IN": "Vanakkam {{parentName}}. Naan {{schoolName}} irundhu pesugireen.",
"te-IN": "Namaskaram {{parentName}} garu. Nenu {{schoolName}} nundi call chestunnanu."
},
"languageDetection": {
"enabled": true,
"initialPrompt": "Press 1 for English, 2 for Hindi, 3 for Tamil, 4 for Telugu. English ke liye 1, Hindi ke liye 2 dabayein.",
"dtmfMapping": {
"1": "en-IN",
"2": "hi-IN",
"3": "ta-IN",
"4": "te-IN"
}
}
}
}
Parent Language Preference Storage#
type ParentPreference struct {
ParentID string
PreferredLang string
PreferredTime string // morning, afternoon, evening
DoNotCall bool
LastContact time.Time
}
func getAgentConfigForParent(parentID string) (*AgentConfig, error) {
pref, err := preferenceService.Get(parentID)
if err != nil {
// Default to Hindi
return getDefaultConfig("hi-IN"), nil
}
config := getDefaultConfig(pref.PreferredLang)
config.CallWindow = pref.PreferredTime
return config, nil
}
Outbound Campaign Configuration#
Fee Reminder Campaign#
Campaigns are created in the dashboard, not through the API. Open the Campaigns page, pick the agent, upload your contact CSV (one column per variable, e.g. parentName, studentName, feeAmount, dueDate), and set the calling window and retries. See Creating a Campaign, CSV Upload and Scheduling.
Once it is running, read progress and per-contact outcomes through the API:
# Campaign progress
curl https://voice-agent.edesy.in/api/v1/campaigns/{campaignId} \
-H "Authorization: Bearer YOUR_API_KEY"
# Outcome of every contact
curl https://voice-agent.edesy.in/api/v1/campaigns/{campaignId}/calls \
-H "Authorization: Bearer YOUR_API_KEY"
Exam Results Announcement#
Use the same flow with a results agent and a CSV holding parentName, studentName, percentage and rank.
Analytics and Metrics#
Key Performance Indicators#
type EducationMetrics struct {
// Admission Metrics
InquiryCalls int
InquiryToVisitRate float64 // % of calls that book campus visits
VisitToAdmissionRate float64 // % of visits that convert
AverageCallDuration time.Duration
// Fee Collection Metrics
TotalReminderCalls int
SuccessfulConnects int // Actually spoke to parent
PaymentPromises int
PromiseToPaymentRate float64 // % of promises that result in payment
CollectionAmount float64 // Total collected after calls
// Efficiency Metrics
CostPerCall float64
CostPerConversion float64
HumanTransferRate float64
}
func trackEducationMetrics(call *Call) {
// Track by campaign type
switch call.CampaignType {
case "admission_inquiry":
metrics.Increment("education.admission.calls")
if call.VisitScheduled {
metrics.Increment("education.admission.visits_scheduled")
}
case "fee_reminder":
metrics.Increment("education.fees.calls")
if call.PaymentPromise {
metrics.Increment("education.fees.promises")
metrics.Gauge("education.fees.promised_amount", call.PromisedAmount)
}
if call.PaymentLinkSent {
metrics.Increment("education.fees.links_sent")
}
}
// Common metrics
metrics.Histogram("education.call_duration", call.Duration.Seconds())
if call.TransferredToHuman {
metrics.Increment("education.human_transfers")
}
}
Fee Collection Dashboard#
type FeeCollectionReport struct {
Date time.Time
TotalCalls int
Connected int
NotAnswered int
Busy int
PaymentPromises int
PromisedAmount float64
PaymentLinksSet int
ActualPayments int // Payments received
CollectedAmount float64 // After 48 hours of call
ConversionRate float64 // Promises that paid
ROI float64 // Collection / Cost
}
func generateFeeReport(campaignID string, date time.Time) FeeCollectionReport {
calls := getCampaignCalls(campaignID, date)
report := FeeCollectionReport{Date: date}
for _, call := range calls {
report.TotalCalls++
switch call.Disposition {
case "connected":
report.Connected++
case "no_answer":
report.NotAnswered++
case "busy":
report.Busy++
}
if call.PaymentPromise {
report.PaymentPromises++
report.PromisedAmount += call.PromisedAmount
}
}
// Check actual payments (async, after 48 hours)
payments := paymentService.GetPaymentsForCampaign(campaignID, date)
report.ActualPayments = len(payments)
for _, p := range payments {
report.CollectedAmount += p.Amount
}
if report.PaymentPromises > 0 {
report.ConversionRate = float64(report.ActualPayments) / float64(report.PaymentPromises)
}
callCost := float64(report.TotalCalls) * 2.5 // Rs. 2.5 per call
if callCost > 0 {
report.ROI = report.CollectedAmount / callCost
}
return report
}
Admission Funnel Tracking#
type AdmissionFunnel struct {
Inquiries int // Total inquiry calls
Qualified int // Interested + eligible
VisitsScheduled int // Campus visits booked
VisitsCompleted int // Actually visited
ApplicationsFiled int // Submitted application
Admissions int // Final admissions
}
func trackAdmissionConversion(inquiry *Inquiry) {
// Stage 1: Inquiry
metrics.Increment("admission.funnel.inquiries")
// Stage 2: Qualification
if inquiry.IsQualified() {
metrics.Increment("admission.funnel.qualified")
}
// Stage 3: Visit Scheduled
if inquiry.VisitScheduled {
metrics.Increment("admission.funnel.visits_scheduled")
}
}
// Webhook handler for downstream events
func handleAdmissionEvent(event AdmissionEvent) {
switch event.Type {
case "visit_completed":
metrics.Increment("admission.funnel.visits_completed")
case "application_submitted":
metrics.Increment("admission.funnel.applications")
case "admission_confirmed":
metrics.Increment("admission.funnel.admissions")
metrics.Gauge("admission.conversion_value", event.FeeAmount)
}
}
Best Practices#
1. Respectful Communication#
// Always use respectful honorifics
var honorifics = map[string]string{
"hi-IN": "ji", // Sharma ji
"ta-IN": "avargal", // Sharma avargal
"te-IN": "garu", // Sharma garu
"bn-IN": "babu", // Sharma babu
}
// Time-appropriate greetings
func getGreeting(lang string, hour int) string {
greetings := map[string]map[string]string{
"hi-IN": {
"morning": "Suprabhat",
"afternoon": "Namaskar",
"evening": "Shubh sandhya",
},
"en-IN": {
"morning": "Good morning",
"afternoon": "Good afternoon",
"evening": "Good evening",
},
}
period := "afternoon"
if hour < 12 {
period = "morning"
} else if hour >= 17 {
period = "evening"
}
return greetings[lang][period]
}
2. Optimal Call Timing#
// Best times to reach parents
var optimalCallWindows = map[string]TimeWindow{
"working_parents": {
Start: "18:00",
End: "20:00",
Days: []string{"monday", "tuesday", "wednesday", "thursday", "friday"},
},
"homemakers": {
Start: "10:00",
End: "12:00",
Days: []string{"monday", "tuesday", "wednesday", "thursday", "friday"},
},
"weekends": {
Start: "10:00",
End: "13:00",
Days: []string{"saturday"},
},
}
// Avoid calling during
var blackoutPeriods = []string{
"school_hours", // Parents may be in meetings
"lunch_time", // 13:00-14:00
"early_morning", // Before 09:00
"late_evening", // After 21:00
"exam_days", // When students have exams
}
3. Handle Sensitive Situations#
// Fee defaulters - extra care needed
var feeReminderGuidelines = `
For parents with overdue fees:
- Never shame or embarrass
- Always offer payment plans
- Transfer to accounts if needed
- Note: Some families face genuine hardship
Phrases to AVOID:
- "Your fees are very late"
- "We may have to take action"
- "Other parents have paid"
Phrases to USE:
- "I'm calling to help with payment options"
- "We have flexible payment plans"
- "How can we work together on this?"
`
// Handle emotional parents
func handleEmotionalCaller(session *Session) {
// Detect distress keywords
distressSignals := []string{"can't afford", "lost job", "financial problem", "please understand"}
for _, signal := range distressSignals {
if strings.Contains(session.LastTranscript, signal) {
// Transfer to human with context
session.Transfer("accounts", "Parent expressing financial difficulty - handle with empathy")
return
}
}
}
4. Data Privacy Compliance#
// Student data is sensitive - follow privacy guidelines
type PrivacyConfig struct {
// Only call registered numbers
VerifyCallerID bool
// Don't share student info without verification
RequireVerification bool
// Log all data access
AuditLogging bool
// Data retention
TranscriptRetention time.Duration // 90 days
RecordingRetention time.Duration // 30 days
}
func verifyParentIdentity(session *Session, studentID string) bool {
student := studentService.Get(studentID)
// Verify caller is registered parent/guardian
if session.CallerPhone != student.ParentPhone &&
session.CallerPhone != student.GuardianPhone {
session.TTS.Speak("I'm sorry, I can only share student information with registered parents. Please contact the school office.")
return false
}
return true
}
5. Integration with School Management System#
// Webhook for SIS integration
type SISWebhook struct {
EventType string `json:"event_type"`
StudentID string `json:"student_id"`
CallID string `json:"call_id"`
Timestamp time.Time `json:"timestamp"`
Data any `json:"data"`
}
func handleCallEnded(call *Call) {
// Sync with SIS
webhook := SISWebhook{
EventType: "voice_call_completed",
StudentID: call.StudentID,
CallID: call.ID,
Timestamp: time.Now(),
Data: map[string]any{
"purpose": call.Purpose,
"duration": call.Duration.Seconds(),
"outcome": call.Outcome,
"payment_promise": call.PaymentPromise,
"transcript_summary": call.Summary,
},
}
sisClient.SendWebhook(webhook)
// Update student communication log
sisClient.LogCommunication(call.StudentID, CommunicationLog{
Type: "voice_call",
Date: call.StartTime,
Purpose: call.Purpose,
Notes: call.Summary,
})
}
Sample Integration#
Complete Fee Reminder Flow#
func runFeeReminderCampaign(ctx context.Context) {
// 1. Get pending fees from SIS
pendingFees := sisClient.GetPendingFees(FeeQuery{
DueBefore: time.Now().AddDate(0, 0, 7), // Due in 7 days
MinAmount: 1000,
})
// 2. Prepare campaign contacts
var contacts []CampaignContact
for _, fee := range pendingFees {
// Get parent preference
pref := preferenceService.Get(fee.ParentID)
if pref.DoNotCall {
continue
}
contacts = append(contacts, CampaignContact{
Phone: fee.ParentPhone,
Variables: map[string]string{
"parentName": fee.ParentName,
"studentName": fee.StudentName,
"studentClass": fee.Class,
"feeType": fee.FeeType,
"feeAmount": fmt.Sprintf("%.0f", fee.Amount),
"dueDate": fee.DueDate.Format("2 January"),
},
Language: pref.PreferredLang,
})
}
// 3. Create campaign
campaign := Campaign{
Name: fmt.Sprintf("Fee Reminder - %s", time.Now().Format("Jan 2006")),
AgentID: "fee_reminder_hindi",
Contacts: contacts,
Schedule: CampaignSchedule{
CallWindow: TimeWindow{Start: "10:00", End: "18:00"},
ExcludeDays: []string{"sunday"},
},
}
campaignService.Start(ctx, campaign)
// 4. Monitor and report
go monitorCampaign(campaign.ID)
}
Next Steps#
- Indian Languages - Multi-language configuration
- Function Calling - Tool integration
- Outbound Sales - Campaign management
- Webhooks - SIS integration