Effective followup is critical for obtaining complete case information and ensuring high-quality safety data. VIGIA automates followup scheduling, tracks communications, and manages missing data requests.
Overview
VIGIA’s followup system provides:
Automated Scheduling : ICH E2D guideline-compliant followup timelines (Day 0, 30, 60, 90)
Multi-channel Communication : Email, SMS, WhatsApp
Missing Data Detection : AI-powered identification of incomplete fields
Thread Management : Complete communication history per case
Status Tracking : Pending, Sent, Responded, Completed
Followup Workflow
Initiating Followup
Access Case Details
Open the case requiring followup: URL : /casos/{icsr_id}
Navigate to Followup Tab
Click Seguimiento (Followup) tab The interface shows:
Message Thread : All sent and received messages
Followup Plan : Scheduled attempts and their status
Missing Data : Detected incomplete fields
Quick Actions : Send message, schedule attempt, mark complete
Review Missing Data
VIGIA automatically detects missing critical fields: compute_missing(icsr) → {
"paciente" : [ "peso" , "fecha_nacimiento" ],
"producto" : [ "dosis" , "via_administracion" ],
"evento" : [ "fecha_fin" , "desenlace" ],
"reporter" : [ "profesion" ]
}
The UI displays: Información Faltante (4 campos) Paciente :
Peso
Fecha de nacimiento exacta
Producto :
Dosis exacta y frecuencia
Vía de administración
Evento :
Fecha de resolución
Desenlace actual
Generate Followup Message
Click Generate Request . The system auto-generates a message: build_missing_email_body(icsr, missing_fields) → str
Example output :Estimado(a) reportante,
Gracias por notificar el evento adverso relacionado con Paracetamol.
Para completar la evaluación del caso, necesitamos la siguiente información adicional:
INFORMACIÓN DEL PACIENTE:
- Peso aproximado
- Fecha de nacimiento (o edad exacta)
INFORMACIÓN DEL MEDICAMENTO:
- Dosis administrada (mg o tabletas)
- Vía de administración (oral, inyectable, etc.)
INFORMACIÓN DEL EVENTO:
- ¿Cuándo se resolvió el evento? (fecha aproximada)
- Estado actual del paciente (recuperado, en recuperación, etc.)
Puede responder a este correo con la información.
Atentamente,
Departamento de Farmacovigilancia
Select Channel
Choose communication channel based on reportante_contacto: Available channels :Channel: [Email ▼]
Options:
- Email (recommended)
- SMS
- WhatsApp
The system extracts contact info: extract_channels(icsr.reportante_contacto) → {
"email" : "[email protected] " ,
"phone" : "+51999999999" ,
"whatsapp" : "+51999999999"
}
Send Message
Click Send . The system:
Sends via selected channel :
POST / api / v1 / icsr / {icsr_id} / followup / send
Body: {
"channel" : "email" ,
"body" : "..." ,
"subject" : "[VIGIA] Solicitud de información - ICSR #123"
}
Logs outbound message :
FollowupMessage(
icsr_id = 123 ,
channel = "email" ,
direction = "out" ,
body = "..." ,
external_id = "<[email protected] >" ,
status = "sent" ,
created_at = datetime.now()
)
Updates case status :
Changes estado from “Pendiente” to “En proceso”
Records followup attempt
Email threading : Uses RFC 2822 headers for thread continuity:
In - Reply - To: < previous - message - id >
References: < msg - 1 > < msg - 2 > < msg - 3 >
Schedule Next Attempt
If no response, schedule the next followup: Click Schedule > Select date: Next Attempt: [2024-04-05 ▼] (30 days from now)
Standard timeline (ICH E2D):
Day 0 : Initial request (sent)
Day 30 : First followup
Day 60 : Second followup
Day 90 : Final followup
Day 120 : Close case if no response
Automated Followup Plans
Creating a Followup Plan
VIGIA can automatically schedule multiple attempts:
Define Plan Template
In Admin > Settings > Followup Plans , create a template: {
"name" : "Standard ICH E2D" ,
"ref" : "8.4.1" ,
"attempts" : [
{ "label" : "Initial Request" , "afterHours" : 0 },
{ "label" : "First Followup" , "afterHours" : 720 }, // 30 days
{ "label" : "Second Followup" , "afterHours" : 1440 }, // 60 days
{ "label" : "Final Followup" , "afterHours" : 2160 } // 90 days
],
"notes" : "ICH E2D guideline compliance"
}
Assign Plan to Case
In the Seguimiento tab, click Apply Plan > select template: Followup Plan: [Standard ICH E2D ▼]
Base Date: [2024-03-01 ▼] (case reception date)
The system calculates absolute dates: ✓ Initial Request: 2024-03-01 (sent)
⏱ First Followup: 2024-03-31 (pending)
⏱ Second Followup: 2024-04-30 (pending)
⏱ Final Followup: 2024-05-30 (pending)
Automated Execution
VIGIA’s followup dispatcher runs daily: # backend/app/services/followup_dispatch.py
def dispatch_due_followups ():
"""Send followup messages for attempts due today"""
due = get_attempts_due_today()
for attempt in due:
if attempt.status == "pending" :
send_followup_message(
icsr_id = attempt.icsr_id,
message = attempt.message,
channel = attempt.channel
)
attempt.status = "sent"
attempt.sent_at = datetime.now()
Cron schedule : 0 9 * * * (daily at 9 AM)
Manual Override
You can manually trigger or skip attempts:
Send Now : Click ▶ icon (sends immediately, ignores schedule)
Skip : Click ⏭ icon (marks as skipped, moves to next)
Reschedule : Click 📅 icon (change date)
Mark Complete : Click ✓ icon (if info received via other channel)
Message Thread Management
Viewing Communication History
The Seguimiento tab displays a chronological thread:
┌─────────────────────────────────────────────────────────┐
│ 📨 OUT | Email | 2024-03-01 10:15 │
│ To: [email protected] │
│ Subject: [VIGIA] Solicitud de información - ICSR #123 │
│ │
│ Estimado reportante, │
│ Necesitamos información adicional sobre el caso... │
│ [Show Full Message] │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 📬 IN | Email | 2024-03-05 14:32 │
│ From: [email protected] │
│ Re: [VIGIA] Solicitud de información - ICSR #123 │
│ │
│ Hola, adjunto la información solicitada: │
│ - Peso: 68 kg │
│ - Dosis: 500 mg cada 8 horas │
│ - Evento resuelto el 2024-02-28 │
│ [Show Full Message] [Update Case] │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 📨 OUT | Email | 2024-03-31 09:00 │
│ To: [email protected] │
│ Subject: [VIGIA] Recordatorio - ICSR #123 │
│ [Scheduled - First Followup] │
└─────────────────────────────────────────────────────────┘
API Endpoint :
GET / api / v1 / icsr / {icsr_id} / followup / thread
Response: {
"items" : [
{
"id" : 1 ,
"icsr_id" : 123 ,
"channel" : "email" ,
"direction" : "out" ,
"body" : "..." ,
"external_id" : "<[email protected] >" ,
"created_at" : "2024-03-01T10:15:00Z"
},
...
],
"total" : 2
}
Processing Inbound Responses
When a reporter responds via email:
Email Polling
VIGIA polls inbox for new messages: # backend/app/services/email_ingestion.py
def poll_inbox ():
messages = fetch_new_emails()
for msg in messages:
# Check if In-Reply-To matches existing case
parent_id = msg.headers.get( "In-Reply-To" )
icsr = find_icsr_by_message_id(parent_id)
if icsr:
log_inbound_message(icsr.id, msg)
notify_evaluator(icsr)
Message Logging
Inbound message is added to thread: FollowupMessage(
icsr_id = 123 ,
channel = "email" ,
direction = "in" ,
body = msg.body,
external_id = msg.message_id,
external_meta = json.dumps({
"from" : msg.from_address,
"subject" : msg.subject,
"references" : msg.references
})
)
Notification
Assigned evaluator receives alert: Email :Subject: [VIGIA] Respuesta recibida - ICSR #123
El reportante ha respondido a la solicitud de seguimiento.
Caso: #123
Producto: Paracetamol
Revisa la respuesta en VIGIA y actualiza el caso.
Dashboard badge :Casos (3 nuevas respuestas) 🔴
Manual Update
Evaluator reviews response and updates case:
Open case detail
Click Update from Followup (auto-fill available)
Review extracted data
Save changes
Mark followup as Completed
Multi-Channel Support
Email (Recommended)
Advantages :
Threaded conversations (In-Reply-To headers)
Attachment support
Professional appearance
Audit trail
Configuration : See Case Intake - Email
SMS
Use cases :
Quick reminders
No email available
Rural areas with limited internet
Limitations :
160 character limit (split into multiple for longer messages)
No attachments
Higher cost per message
Setup :
Configure SMS gateway in Admin > Settings > SMS
Supported providers: Twilio, AWS SNS, local SMS gateway
Set reportante_contacto to phone number format: +51999999999
Sending :
POST / api / v1 / icsr / {icsr_id} / followup / send
Body: {
"channel" : "sms" ,
"body" : "VIGIA: Necesitamos info adicional del caso #123. Responda o llame al 01-234-5678."
}
WhatsApp
Advantages :
High open rates (98%)
Multimedia support (images, documents)
Popular in Latin America
Read receipts
Requirements :
WhatsApp Business API account
Approved message templates (for proactive outreach)
Setup :
Get WhatsApp Business API credentials
Configure webhook: https://your-vigia.com/api/v1/webhooks/whatsapp
Create message templates in WhatsApp Manager
Approve templates (required for first contact)
Message Templates :
{
"name" : "followup_request" ,
"language" : "es" ,
"components" : [
{
"type" : "body" ,
"text" : "Hola {{1}}, necesitamos información adicional sobre el caso {{2}}. ¿Puede proporcionarnos {{3}}?"
}
]
}
Sending :
POST / api / v1 / icsr / {icsr_id} / followup / send
Body: {
"channel" : "whatsapp" ,
"template" : "followup_request" ,
"parameters" : [ "Juan" , "#123" , "la dosis del medicamento" ]
}
Followup Status Tracking
Status Values
Status Meaning Actions Pending Scheduled but not sent Send now, Reschedule, Skip Sent Message delivered Wait for response, Resend Responded Reporter replied Update case, Mark complete Completed Info received and case updated Archive Skipped Manually skipped Re-activate if needed Failed Delivery failed (bounce, invalid address) Update contact, Retry
Updating Status
Automatically :
Pending → Sent when message is delivered
Sent → Responded when reply received (email threading)
Responded → Completed when case updated
Manually :
PATCH / api / v1 / icsr / {icsr_id} / followup / {message_id}
Body: { "status" : "completed" }
Closing Followup
When to Close
Close followup when:
✅ All missing information obtained
✅ Case is complete and ready for reporting
⏰ Maximum attempts reached (e.g., 90 days, no response)
🚫 Reporter explicitly declined to provide more info
🚫 Reporter is unreachable (invalid contact info)
Closure Workflow
Mark as Complete
In Seguimiento tab, click Close Followup
Select Closure Reason
Reason: [Info Complete ▼]
Options:
- Info Complete (all data obtained)
- Max Attempts Reached (no response after 90 days)
- Reporter Declined
- Reporter Unreachable
- Case Closed (duplicate, invalid, etc.)
Add Closure Notes
Document the outcome: Closure Notes:
Información completa obtenida tras segundo seguimiento.
Paciente confirmó recuperación total sin secuelas.
Caso listo para reporte DIGEMID.
Update Case Status
The system:
Sets followup_status = "Closed"
Updates estado = "Completado" (if all required info present)
Cancels pending scheduled attempts
Logs closure in audit trail
Quality Assurance
Before closing followup:
All critical missing fields addressed or documented as unavailable
Minimum 2 attempts made (Day 0 + Day 30) for serious cases
Final status clear : Complete, No Response, or Declined
Closure notes document outcome and any outstanding issues
Case ready for next workflow step (causality, reporting)
Troubleshooting
Reporter not receiving emails
Check :
Email address correct in reportante_contacto
Check spam/junk folder (ask reporter)
Verify SMTP send logs: backend/logs/mail.log
Test email delivery: POST /api/v1/admin/mail/test
Fix :
Update email address if incorrect
Add VIGIA domain to reporter’s whitelist
Use alternative channel (SMS, WhatsApp)
Email thread not linking correctly
Cause : Missing or incorrect In-Reply-To headerFix :
Check that external_id is saved when sending
Verify mail server supports RFC 2822 headers
Manual linking: In message thread, click Link and select parent message
Scheduled followups not sending
Check :
Cron job running: systemctl status vigia-followup-dispatcher
Logs: backend/logs/followup_dispatch.log
Attempt status: should be “pending” not “skipped”
Fix :# Manually trigger dispatcher
python -m app.tasks.followup_dispatch
# Or via API
POST /api/v1/admin/followup/dispatch
API Reference
# Get followup thread
GET / api / v1 / icsr / {icsr_id} / followup / thread
# Send followup message
POST / api / v1 / icsr / {icsr_id} / followup / send
Body: { "channel" : "email" , "body" : "..." }
# Update message status
PATCH / api / v1 / icsr / {icsr_id} / followup / {message_id}
Body: { "status" : "completed" }
# Apply followup plan
POST / api / v1 / icsr / {icsr_id} / followup / plan
Body: { "plan_id" : 1 , "base_at" : "2024-03-01T00:00:00Z" }
# Close followup
POST / api / v1 / icsr / {icsr_id} / followup / close
Body: { "reason" : "complete" , "notes" : "..." }