AutoSendLetters
Overview
AutoSendLettersTask generates and sends patient cost estimate letters for visits in the batch table based on configured AutoSendLetterConfig criteria. It evaluates visits against qualification rules, generates personalized letters with cost estimates, and sends them via email or SMS based on patient preferences. This task can also regenerate letters when visit details change according to configured resend criteria.
Use this task when: You need to process cost estimate letters for visits that have been queued in the batch table, either for initial sending or regeneration after visit changes (charges, benefits, facilities, etc.).
Don't use this task when:
- Processing future visits in real-time (use
AutoSendLettersForFutureVisitsTaskinstead which queries future visits directly) - You need to generate letters without any qualification logic (create letters manually via UI or API)
- Visits haven't been calculated yet (letters require completed estimate calculations)
Parameters
This task has no configurable properties. All behavior is controlled by AutoSendLetterConfig entities stored in the database, which define:
- Visit qualification criteria (facilities, insurances, providers, visit types/statuses, date ranges, amount thresholds, patient age)
- Letter template to use
- Whether to auto-generate only (without sending) or to generate and send
- Resend criteria for letter regeneration when visit details change
- GFE (Good Faith Estimate) compliance settings
The task operates on visits in the VisitBatch table, processing all visits that have been queued for letter generation.
Execution Flow
Processing Algorithm
The task uses a multi-phase processing algorithm to ensure correct letter generation and sending:
Phase 1: Configuration Matching
- Load all
AutoSendLetterConfigrecords from database - Filter out GFE-only configs if customer doesn't have GFE enabled
- For each visit in batch, find the highest-priority matching config based on:
- Facility, Insurance, Provider matches (empty list = all qualify)
- Visit type, status, scheduled date range
- Deposit amount and total patient amount ranges
- Patient age range
- Custom property values
- GFE visit filter state
Phase 2: Letter Generation Decision
For each visit that matched a config, determine if a new letter should be generated:
- Generate if: Visit has no letter yet OR
- Generate if: Visit has a letter AND patient can receive it (has consent + valid contact) AND config is not AutoGenerateOnly OR
- Generate if: Visit has a letter AND config is AutoGenerateOnly AND visit has qualifying changes (based on VisitResendCriteria)
Phase 3: Letter Sending Decision
For each generated letter, determine if it should be sent:
- Send if: No errors exist AND config is not AutoGenerateOnly
- Don't send if: Patient lacks electronic communication consent
- Don't send if: Patient lacks valid email/phone/birthdate
- Don't send if: Visit not calculated or has unresolved benefits
- Don't send if: Config has AutoGenerateOnly = true
Example Execution
Given:
- 100 visits in VisitBatch
- 3 AutoSendLetterConfigs:
- Config A: Facilities [FAC-01], MinDaysOut=1, MaxDaysOut=7
- Config B: Insurances [INS-SELF], AutoGenerateOnly=true
- Config C: MinTotalPatientAmount=500, VisitResendCriteria=[ChargesChanged]
Processing:
- Visit V-1: FAC-01, 3 days out, $1000 → Matches Config A → First letter → Generate & Send
- Visit V-2: FAC-02, Self-pay → Matches Config B → Generate but Don't Send (AutoGenerateOnly)
- Visit V-3: FAC-01, already sent letter, charges changed → Matches Config C → Regenerate & Resend
- Visit V-4: FAC-01, no patient email/phone → Matches Config A → Generate but Don't Send (error: InvalidEmailAndPhone)
- Visit V-5: Not calculated yet → Matches Config A → Don't Generate (error: NotCalculated)
Result: 4 letters generated, 2 sent successfully, 2 saved with errors, 1 skipped
Usage Examples
Example 1: Basic scheduled execution
// Typical scheduled job execution
var task = new AutoSendLettersTask();
task.Execute(customerDataStore);
// Processes all visits in VisitBatch against all active AutoSendLetterConfigs
// Generates and sends letters according to config rules
Example 2: Part of a visit processing pipeline
// After visit calculation, queue for letter sending
var calcTask = new BatchProcessAccountDataTask
{
// ... calculation parameters
};
calcTask.Execute(customerDataStore);
// Send letters for newly calculated visits
var letterTask = new AutoSendLettersTask();
letterTask.Execute(customerDataStore);
Example 3: After configuration changes
// After updating AutoSendLetterConfig, reprocess affected visits
var reprocessTask = new ReprocessChangedConfigurationsTask
{
ReprocessVisits = true,
CustomerKey = "CUST-01"
};
reprocessTask.Execute(customerDataStore);
// Generate letters for newly qualifying visits
var letterTask = new AutoSendLettersTask();
letterTask.Execute(customerDataStore);
Example 4: With visit change tracking for resends
// Update visit charges (creates VisitChangeEvent)
visit.TotalPatientAmount = 1500;
visitRepo.Save(visit, batchMode: true);
// When AutoSendLetters runs, it checks VisitChangeEvents
// If visit has VisitResendCriteria = [ChargesChanged], letter regenerates
var letterTask = new AutoSendLettersTask();
letterTask.Execute(customerDataStore);
// Visit V-123's letter will regenerate because charges changed
Related Tasks Comparison
| Task | Scope | Qualification Logic | Custom SQL | GFE Support |
|---|---|---|---|---|
| AutoSendLetters | Batch table visits | AutoSendLetterConfig criteria | No | Yes (sets GFE compliance) |
AutoSendLettersForFutureVisitsTask |
All future visits (scheduled > now) | Same AutoSendLetterConfig criteria | No | Yes (sets GFE compliance for future) |
SendHL7LettersTask |
HL7OutboundLetter records | No qualification (sends pre-created letters) | No | N/A (downstream task) |
Key Differences:
- AutoSendLetters processes only visits in the
VisitBatchtable using batch visit queries - AutoSendLettersForFutureVisitsTask processes all visits with
ScheduledTime > DateTime.Nowusing real-time visit queries - Both tasks use the same
AutoSendLetterscore class with differentIAutoSendLetterItemGeneratorimplementations:- AutoSendLetters uses
AutoSendLetterItemGenerator(queries batch visits) - AutoSendLettersForFutureVisits uses
FutureVisitAutoSendLetterItemGenerator(queries future visits)
- AutoSendLetters uses
- SendHL7LettersTask is a downstream task that processes
HL7OutboundLetterrecords created by AutoSendLetters
When to choose which:
- Use AutoSendLetters for scheduled batch processing of visits already queued in VisitBatch
- Use AutoSendLettersForFutureVisits for real-time processing of upcoming appointments without batch queuing
- Use SendHL7LettersTask to transmit letters via HL7 after AutoSendLetters creates HL7OutboundLetter records
Performance Considerations
Database Impact
- Tables Read:
VisitBatch,PatientLetterBatch,QuoteBatch,VisitChangeEventBatch(batch tables)AutoSendLetterConfig,LetterTemplate,Patient,Facility,Insurance,ProviderCustomPropertyMap(for custom property matching)
- Tables Written:
PatientLetter(one per generated letter)PatientLetterSendEvent(one per visit processed)VisitChangeEvent(flags events that triggered regeneration)Visit(updatesLetterSent,LetterRequired,AutoSendLetterConfigKey)HL7OutboundLetter(if HL7 message generation configured)MessageAPILog(API response tracking)VisitGFECompliance(GFE compliance tracking)
- Bulk Operations: Uses batch saves for visits and change events
- Query Complexity: Moderate - joins across Patient, Visit, Quote, and Letter tables
Optimization Tips
| Scenario | Recommendation | Rationale |
|---|---|---|
| Large batch (>10,000 visits) | Process in smaller chunks by queuing subsets of visits to batch table | Reduces memory usage and transaction duration |
| Multiple configs with overlapping criteria | Order configs by priority to control which config wins for multi-match visits | First matching config (by priority) is used |
| Frequent resends due to visit changes | Use targeted VisitResendCriteria (e.g., only ChargesChanged) instead of broad criteria | Reduces unnecessary letter regeneration |
| High letter volume | Consider AutoGenerateOnly configs to separate generation from sending | Allows review before bulk sending |
| API rate limits | Stagger task execution or implement custom throttling in letter sender | Prevents API quota exhaustion |
External API Impact
- Email API (AWS SES/SMTP): One API call per sent email letter
- SMS API (Twilio/etc): One API call per sent SMS letter
- TinyUrl API: One call per letter to generate shortened payment link
- Patient Portal API: Pushes letter data for portal display
- Rate limiting and retry logic handled by
LetterSenderFactoryandLetterHandler