# Fix Batch SMS Error Handling Plan

## Problem
Batch SMS endpoints have incomplete error handling:

1. **DB operations after SMS send have no try-catch**
   - If SMS succeeds but DB insert fails, message sent but not tracked
   - Data inconsistency: SMS received by customer but no record in system

2. **No alerting for failures**
   - High failure rates go unnoticed
   - Partial batch failures not flagged

**Affected Endpoints:**
- `POST /api/sms/send-batch` (lines 470-595)
- `POST /api/sms/batch` (lines 687-813)

## Severity
**MEDIUM** - Data inconsistency possible, but SMS send/fail is tracked.

## Current Behavior
```javascript
// SMS sent successfully
const result = await sendSMS(toPhone, message);

if (result.success) {
  // DB insert - NO ERROR HANDLING
  await supabase.from('communications').insert({...});
  // If this fails, SMS was sent but not logged!

  await supabase.from('leads').update({...});
  // If this fails, last_contact_at not updated!
}
```

## Solution
Wrap DB operations in try-catch and log failures.

### Fix for /api/sms/batch (lines 762-794)
```javascript
if (result.success) {
  sent++;

  // Log communication with error handling
  try {
    await supabase
      .from('communications')
      .insert({
        lead_id: msg.leadId,
        type: 'sms_outbound',
        direction: 'outbound',
        phone: msg.to,
        content: msg.body,
        external_id: result.messageSid,
        metadata: {
          template_slug: templateSlug || null,
          batch: true
        }
      });

    // Update lead
    await supabase
      .from('leads')
      .update({
        last_contact_at: new Date().toISOString(),
        last_contact_type: 'sms_outbound'
      })
      .eq('id', msg.leadId);
  } catch (dbError) {
    console.error(`[BatchSMS] DB error for lead ${msg.leadId}:`, dbError.message);
    // SMS was sent but DB failed - flag it
    results.push({
      leadId: msg.leadId,
      success: true,
      messageSid: result.messageSid,
      warning: 'SMS sent but failed to log to database'
    });
    continue;
  }

  results.push({ leadId: msg.leadId, success: true, messageSid: result.messageSid });
} else {
  failed++;
  results.push({ leadId: msg.leadId, success: false, error: result.error });
}
```

### Add Slack Alert for High Failure Rate
```javascript
// After the loop, check failure rate
if (failed > 0 && failed / messages.length > 0.5) {
  // More than 50% failed - alert
  sendSlackAlert({
    channel: 'growth-alerts',
    text: `⚠️ Batch SMS high failure rate: ${failed}/${messages.length} failed`,
    blocks: [{
      type: 'section',
      text: { type: 'mrkdwn', text: `*Batch SMS Alert*\n• Sent: ${sent}\n• Failed: ${failed}\n• Rate: ${Math.round(failed/messages.length*100)}%` }
    }]
  }).catch(() => {}); // Don't fail request for alert failure
}
```

## Implementation Steps
- [ ] Phase 1: Add try-catch around DB operations in `/api/sms/batch`
- [ ] Phase 2: Add try-catch around DB operations in `/api/sms/send-batch`
- [ ] Phase 3: Add warning flag in results for DB failures
- [ ] Phase 4: Add Slack alert for >50% failure rate
- [ ] Phase 5: Test with simulated DB failure

## Files to Modify
| File | Lines | Change |
|------|-------|--------|
| `src/routes/sms.js` | 762-794 | Add try-catch around DB ops in /batch |
| `src/routes/sms.js` | 536-570 | Add try-catch around DB ops in /send-batch |

## Database Migration
None required.

## Success Criteria
1. DB failures don't crash batch operation
2. Results include warning for partial DB failures
3. Slack alert fires when >50% fail
4. All sent SMS are tracked (or flagged if tracking failed)

## Notes for Builder
- Import slack service if not already imported
- Use existing `sendSlackMessage` function from `src/services/slack.js`
- Keep the 100ms delay between messages

## Notes for QA
- Test batch with invalid lead ID (should track failure)
- Simulate DB timeout to verify error handling
- Check Slack for alerts after high-failure batch

## Why This Matters
- SMS costs money - need to track what was sent
- Untracked SMS = customer received message but rep doesn't know
- Follow-up workflows won't trigger if last_contact_at not updated
