Workflows & Automation
Last updated: 10/20/2018
Workflows & Automation
Overview
CareHub uses an event-driven workflow engine to automate clinical and administrative processes. Workflows reduce manual work, ensure timely communication, maintain compliance, and provide detailed audit trails.
Workflow Engine ✅ IMPLEMENTED
Inngest Event-Driven Architecture
The platform uses Inngest for workflow orchestration, providing:
- Typed Events: Strongly-typed TypeScript event definitions
- Durable Functions: Automatic retry and error handling
- Step Functions: Long-running workflows with sleep/wait capabilities
- Run Tracking: Database-backed workflow execution history
- Configurable: Admin-editable prompts and thresholds
// Event-driven workflow pattern
export const videoCompleteWorkflow = inngest.createFunction(
{
id: "video-complete",
name: "Video Session Complete",
retries: 3,
},
{ event: "carehub/video.completed" },
async ({ event, step }) => {
// Multi-step workflow with durable execution
const recordings = await step.run("fetch-recordings", async () => {
return await getRoomRecordings(event.data.roomSid);
});
// Wait for external process
await step.sleep("wait-for-processing", "30s");
// Continue workflow...
}
);
Event Types
| Event | Description | Trigger |
|---|---|---|
carehub/intake.completed |
Intake form submitted | Form submission |
carehub/session.completed |
Session marked complete | Provider action |
carehub/session.scheduled |
Session scheduled | Calendar booking |
carehub/video.completed |
Video session ended | Twilio webhook |
carehub/authorization.expiring |
Sessions running low | System check |
carehub/risk.elevated |
Risk level increased | Assessment update |
carehub/checkin.submitted |
Member check-in | Portal submission |
carehub/case.assigned |
Staff assigned to case | Admin action |
carehub/case.discharged |
Case discharged | Status change |
carehub/cron.daily |
Daily scheduled check | Cron trigger |
Workflow Configuration
Workflows are configurable via the Admin portal:
// Get workflow-specific config
const threshold = await getWorkflowConfig<number>(
"authorization-expiring",
"warning_threshold"
);
// Get and interpolate prompt template
const prompt = await getPrompt(
"intake-complete",
"intake_analysis_prompt",
{ primaryConcern, symptoms, phq9Score }
);
Config Types:
STRING- Text valuesNUMBER- Numeric thresholdsBOOLEAN- Feature flagsJSON- Complex configurationPROMPT- LLM prompt templates with{{variable}}interpolation
Workflow Summary
| Workflow | Status | Trigger | Key Actions |
|---|---|---|---|
| Intake Complete | ✅ IMPLEMENTED | Form submission | AI analysis, risk assessment, notify coordinator |
| Session Complete | ✅ IMPLEMENTED | Session marked done | AI summary, PR-2 generation, update progress |
| Session Reminder | ✅ IMPLEMENTED | Session scheduled | 24h reminder, session-time reminder |
| Video Session Complete | ✅ IMPLEMENTED | Twilio room-ended | Fetch recordings, transcribe, trigger session complete |
| Authorization Expiring | ✅ IMPLEMENTED | Sessions < threshold | Alert provider, coordinator |
| Risk Alert | ✅ IMPLEMENTED | Risk level → HIGH | Notify care team, flag for safety planning |
| Check-in Submitted | ✅ IMPLEMENTED | Member check-in | Process mood/pain data, trend analysis |
| RFA Workflow | ✅ IMPLEMENTED | LPCC submits RFA | Psych review, UR submission |
| Staff Assignment | 📋 PLANNED | Staff assigned | Notify staff, log activity |
| No-Show Follow-up | 📋 PLANNED | Session marked no-show | Alert provider, contact member |
| Discharge | 📋 PLANNED | Case → Discharged | Deactivate access, generate docs |
Workflow 1: Intake Complete ✅ IMPLEMENTED
Trigger
carehub/intake.completed - Member submits final step of intake form
Steps
┌─────────────────────┐
│ Intake Submitted │
└──────────┬──────────┘
▼
┌─────────────────────┐
│ AI Analysis │ ← Configurable prompt template
│ - Risk assessment │
│ - Clinical summary│
│ - Recommendations │
└──────────┬──────────┘
▼
┌─────────────────────┐
│ Update Case │
│ - Risk level │
│ - AI summary │
│ - Phase → EVAL │
└──────────┬──────────┘
▼
Risk = HIGH?
│ │
Yes│ │No
▼ ▼
┌─────────────┐ ┌─────────┐
│Notify Coord │ │ Done │
└─────────────┘ └─────────┘
Event Data
interface IntakeCompletedEvent {
caseId: string;
memberId: string;
intakeFormId: string;
primaryConcern: string;
symptoms: string;
phq9Score?: number;
gad7Score?: number;
suicidalIdeation: boolean;
homicidalIdeation: boolean;
safetyPlan?: string | null;
substanceUse?: { alcohol?: string; drugs?: string; tobacco?: string };
familyMhHistory?: string | null;
// WC causation factors
workCausation?: number;
personnelActions?: { discipline?: boolean; termination?: boolean; details?: string };
nonIndustrialStressors?: { financialStress?: boolean; relationshipIssues?: boolean };
}
Configurable Settings
| Key | Type | Default | Description |
|---|---|---|---|
intake_analysis_prompt |
PROMPT | (see below) | AI analysis prompt template |
notify_on_high_risk |
BOOLEAN | true |
Send notification for HIGH risk |
Workflow 2: Session Complete ✅ IMPLEMENTED
Trigger
carehub/session.completed - Provider marks session as completed OR video session ends
Steps
- Summarize Session - AI-generated summary from notes/transcript
- Update Progress - Record progress toward care plan goals
- Generate PR-2 - Auto-generate progress report document
- Check Authorization - Verify sessions remaining
Event Data
interface SessionCompletedEvent {
sessionId: string;
caseId: string;
memberId: string;
providerId: string;
sessionNumber: number;
sessionType: string;
duration: number;
notes: string;
transcript?: string; // From video session
}
Configurable Settings
| Key | Type | Default | Description |
|---|---|---|---|
session_summary_prompt |
PROMPT | (see below) | AI summary prompt template |
auto_generate_pr2 |
BOOLEAN | true |
Auto-generate PR-2 document |
Workflow 3: Session Reminder ✅ IMPLEMENTED
Trigger
carehub/session.scheduled - Session is scheduled on the calendar
Steps
┌─────────────────────┐
│ Session Scheduled │
└──────────┬──────────┘
▼
┌─────────────────────┐
│ Parse Date/Time │
│ Calculate reminder │
│ times │
└──────────┬──────────┘
▼
┌──────────┐
│ sleep │ Wait until 24h before
│ until │
└────┬─────┘
▼
┌─────────────────────┐
│ Check Session │ Still scheduled?
│ Status │ Already sent?
└──────────┬──────────┘
▼
Still valid?
│ │
Yes│ │No
▼ ▼
┌─────────────┐ ┌─────────┐
│Send 24h │ │ Skip │
│Reminder │ └─────────┘
│Email > SMS │
└──────┬──────┘
▼
┌──────────┐
│ sleep │ Wait until session time
│ until │
└────┬─────┘
▼
┌─────────────────────┐
│Send Session-Time │
│Reminder │
└─────────────────────┘
Notification Priority
- Email (primary) - If configured
- SMS (fallback) - Via Twilio if email fails/not configured
Session Model Updates
// Reminder tracking fields on Session
reminder24hSent: boolean;
reminderSessionSent: boolean;
Workflow 4: Video Session Complete ✅ IMPLEMENTED
Trigger
carehub/video.completed - Twilio webhook reports room-ended
Architecture
┌─────────────────┐
│ Video Call │
│ (Twilio) │
└────────┬────────┘
│ room-ended webhook
▼
┌─────────────────┐
│ Webhook │
│ Handler │ POST /api/webhooks/twilio/room-status
└────────┬────────┘
│ inngest.send("carehub/video.completed")
▼
┌─────────────────┐
│ Inngest │
│ Workflow │
└────────┬────────┘
│
┌────┴────┬─────────┬──────────┐
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Get │ │ Fetch │ │ Trans- │ │ Trigger│
│Session │ │Record- │ │ cribe │ │Session │
│ │ │ings │ │ Audio │ │Complete│
└────────┘ └────────┘ └────────┘ └────────┘
│
▼
┌─────────────────┐
│ Azure Speech │
│ Batch API │
└─────────────────┘
Steps
- Get Session - Retrieve session details from database
- Wait for Recordings (30s) - Twilio needs time to process
- Fetch Recordings - Get download URLs from Twilio API
- Transcribe - Azure Speech Services batch transcription
- Poll every 15s for up to 10 minutes
- Format transcript with speaker labels
- Update Session - Store transcript, update status
- Trigger Session Complete - Chain to session completion workflow
Event Data
interface VideoCompletedEvent {
sessionId: string;
roomSid: string; // Twilio Room SID
caseId: string;
memberId: string;
providerId: string;
actualDuration: number; // Minutes
}
Session Model Updates
// Video-specific fields
twilioRoomSid: string;
twilioRoomName: string;
twilioRoomStatus: "created" | "in-progress" | "completed";
videoStartedAt: DateTime;
videoEndedAt: DateTime;
videoActualDuration: number;
// Transcript fields
transcript: string;
transcriptStatus: "pending" | "processing" | "completed" | "no-recording" | "not-configured";
transcriptUrl: string;
Workflow 5: Authorization Expiring ✅ IMPLEMENTED
Trigger
carehub/authorization.expiring - Detected during session completion or daily check
Steps
- Check Sessions - Verify remaining session count
- Notify Provider - Alert treating provider via email/in-app
- Draft RFA (optional) - Auto-populate RFA if enabled
Configurable Settings
| Key | Type | Default | Description |
|---|---|---|---|
warning_threshold |
NUMBER | 2 |
Sessions remaining to trigger |
auto_draft_rfa |
BOOLEAN | false |
Auto-create draft RFA |
Workflow 6: Risk Alert ✅ IMPLEMENTED
Trigger
carehub/risk.elevated - Risk level changes to HIGH
Steps
- Confirm Assessment - Validate risk indicators
- Notify Care Team - Alert coordinator and supervisor
- Flag for Safety Planning - Create safety alert on case
Event Data
interface RiskElevatedEvent {
caseId: string;
memberId: string;
previousRiskLevel: "LOW" | "MODERATE" | "HIGH";
newRiskLevel: "HIGH";
source: "intake" | "session" | "checkin" | "manual";
riskIndicators: string[];
}
Configurable Settings
| Key | Type | Default | Description |
|---|---|---|---|
notification_channels |
JSON | ["email", "in_app"] |
Where to send alerts |
escalate_to_supervisor |
BOOLEAN | true |
Include supervisor in notification |
Workflow 7: Check-in Submitted ✅ IMPLEMENTED
Trigger
carehub/checkin.submitted - Member submits weekly check-in
Steps
- Process Check-in - Store mood, pain, sleep data
- Analyze Trends - Compare to previous check-ins
- Flag Concerns - Alert provider if significant decline
Event Data
interface CheckInSubmittedEvent {
checkInId: string;
caseId: string;
memberId: string;
moodLevel: number; // 1-10
painLevel: number; // 0-10
sleepQuality: number; // 1-5
notes?: string;
}
Workflow 8: RFA (Request for Authorization) ✅ IMPLEMENTED
Process Flow
LPCC Portal Psych Portal UR/Payer
│ │ │
▼ │ │
┌──────────────┐ │ │
│ Create Draft │ │ │
│ RFA Form │ │ │
└──────┬───────┘ │ │
│ Submit │ │
▼ │ │
┌──────────────┐ │ │
│ PENDING_ │ ──────────────────►│ │
│ PSYCH_REVIEW │ │ │
└──────────────┘ ▼ │
┌──────────────┐ │
│ Psych Reviews│ │
│ - Clinical │ │
│ - Recommend │ │
└──────┬───────┘ │
│ │
┌──────────┼──────────┐ │
▼ ▼ ▼ │
┌───────────┐┌───────────┐┌───────────┐ │
│ DENIED_BY ││APPROVED_BY││ NEEDS_ │ │
│ PSYCH ││ PSYCH ││ MORE_INFO │ │
└───────────┘└─────┬─────┘└───────────┘ │
│ │
▼ │
┌──────────────┐ │
│ PENDING_UR │ ─────────────────────────►│
│ SUBMISSION │ │
└──────────────┘ ▼
┌──────────────┐
│ UR Decision │
│ - Approve │
│ - Modify │
│ - Deny │
└──────────────┘
RFA Request Model
interface RFARequest {
requestNumber: string; // RFA-2024-XXXX
requestType: "EXTENSION" | "INITIAL" | "MODIFICATION";
// Current status
currentSessionsUsed: number;
currentSessionsAuthorized: number;
additionalSessionsRequested: number;
// Clinical justification
clinicalRationale: string;
treatmentGoals: string;
progressToDate: string;
barriersToClosure: string;
// Current scores
phq9Current: number;
gad7Current: number;
// Status tracking
status: RFAStatus;
requestedAt: DateTime;
reviewedAt?: DateTime;
sessionsApprovedByPsych?: number;
psychReviewNotes?: string;
urApprovedSessions?: number;
urDecisionDate?: DateTime;
}
enum RFAStatus {
DRAFT,
PENDING_PSYCH_REVIEW,
NEEDS_MORE_INFO,
APPROVED_BY_PSYCH,
DENIED_BY_PSYCH,
PENDING_UR_SUBMISSION,
SUBMITTED_TO_UR,
UR_APPROVED,
UR_MODIFIED,
UR_DENIED,
CANCELLED
}
API Routes
| Method | Route | Portal | Description |
|---|---|---|---|
| GET | /api/lpcc/rfa |
LPCC | List LPCC's RFA requests |
| POST | /api/lpcc/rfa |
LPCC | Create new RFA |
| GET | /api/psych/rfa-review |
Psych | Get requests for review |
| PUT | /api/psych/rfa-review/[id] |
Psych | Submit review decision |
Workflow Run Tracking ✅ IMPLEMENTED
All workflows create database records for audit and monitoring:
interface WorkflowRun {
id: string;
workflowId: string; // Link to Workflow definition
inngestRunId?: string; // Inngest execution ID
inngestEventId?: string; // Triggering event ID
// Context
caseId?: string;
memberId?: string;
triggerData: JsonValue; // Event payload
// Execution tracking
status: "PENDING" | "RUNNING" | "COMPLETED" | "FAILED" | "CANCELLED";
currentStep?: string;
stepsCompleted: string[];
stepResults: JsonValue; // Results per step
// Timing
startedAt?: DateTime;
completedAt?: DateTime;
durationMs?: number;
// Error tracking
errorMessage?: string;
errorStep?: string;
errorDetails?: JsonValue;
}
Workflow Health Monitoring
Admin dashboard provides workflow health metrics:
interface WorkflowHealth {
workflowId: string;
slug: string;
name: string;
status: WorkflowStatus;
stats: {
total: number;
completed: number;
failed: number;
running: number;
successRate: number; // Percentage
avgDurationMs: number;
};
recentRuns: WorkflowRun[];
lastRun: Date | null;
lastSuccess: Date | null;
lastFailure: Date | null;
}
Scheduled Jobs
| Job | Schedule | Status | Description |
|---|---|---|---|
| Session Reminders | Event-driven | ✅ IMPLEMENTED | Triggered when session scheduled |
| Authorization Check | Daily 9 AM PT | 📋 PLANNED | Check all cases for low authorization |
| Check-in Reminders | Monday 10 AM PT | 📋 PLANNED | Remind members to check in |
| Overdue Check-ins | Daily 7 AM PT | 📋 PLANNED | Flag members with no recent check-in |
| Report Generation | Weekly Sunday 2 AM PT | 📋 PLANNED | Generate weekly reports |
Notification System
Channels
| Channel | Provider | Status | Use Cases |
|---|---|---|---|
| SMTP/SendGrid | ✅ IMPLEMENTED | Session reminders, alerts | |
| SMS | Twilio | ✅ IMPLEMENTED | Session reminders (fallback) |
| In-App | Activity Feed | ✅ IMPLEMENTED | All notifications |
| Push | 📋 PLANNED | 📋 PLANNED | Mobile alerts |
Notification Fallback Logic
// Email is primary, SMS is fallback
if (emailConfigured) {
emailResult = await sendSessionReminderEmail(sessionId, "24h");
}
if (!emailResult.sent && smsConfigured) {
smsResult = await sendSessionReminder(sessionId, "session");
}
Message Templates
Session Reminder (24h) - SMS:
CareHub Reminder: You have an appointment tomorrow ({date}) at {time}
with {providerName}. Reply CONFIRM or call XXX-XXX-XXXX to reschedule.
Session Reminder (24h) - Email:
Subject: CareHub - Appointment Tomorrow at {time}
Hi {firstName},
This is a reminder that you have an appointment scheduled for tomorrow:
Date: {date}
Time: {time}
Provider: {providerName}
If you need to reschedule, please call us at XXX-XXX-XXXX.
Best regards,
The CareHub Team
Error Handling
All workflows implement:
- Retry Logic - Inngest automatic retries (default 3 attempts)
- Error Recording - Failed runs stored with error details
- Step Isolation - Failures don't affect completed steps
- Alerting - High failure rates trigger admin alerts
try {
// Workflow steps...
await completeWorkflowRun(runId);
} catch (error) {
await failWorkflowRun(
runId,
error instanceof Error ? error.message : "Unknown error",
currentStep,
{ error: String(error) }
);
throw error; // Re-throw for Inngest retry
}
Planned Workflows 📋 PLANNED
Staff Assignment Notification
Trigger: Staff member assigned to case
Steps:
- Send notification email to assigned staff
- Send SMS if enabled
- Log activity
No-Show Follow-up
Trigger: Session marked as No-Show
Steps:
- Alert provider immediately
- Schedule delayed outreach (2 hours)
- Check for no-show pattern (3+ in 30 days)
- Flag compliance concern if pattern detected
Discharge Workflow
Trigger: Case status changed to Discharged
Steps:
- Deactivate member portal access
- Generate discharge documents (Treatment Summary, PR-4)
- Send member notification
- Notify coordinator
- Archive case data
API Routes
Workflow Management (Admin)
| Method | Route | Description |
|---|---|---|
| GET | /api/admin/workflows |
List all workflows with health |
| GET | /api/admin/workflows/[slug] |
Get workflow details |
| PUT | /api/admin/workflows/[slug] |
Update workflow status |
| GET | /api/admin/workflows/[slug]/configs |
Get workflow configs |
| PUT | /api/admin/workflows/[slug]/configs |
Update workflow configs |
| GET | /api/admin/workflows/[slug]/runs |
Get workflow run history |
| POST | /api/admin/workflows/test |
Test workflow execution |
Inngest Endpoint
| Method | Route | Description |
|---|---|---|
| GET/POST | /api/inngest |
Inngest function registration and invocation |
Webhooks
| Method | Route | Provider | Description |
|---|---|---|---|
| POST | /api/webhooks/twilio/room-status |
Twilio | Video room status callbacks |
Development
Running Inngest Dev Server
# Start Inngest dev server for local testing
npx inngest-cli@latest dev
# Or with npm script
npm run inngest:dev
Creating New Workflows
- Define event type in
src/inngest/client.ts - Create function file in
src/inngest/functions/ - Export from
src/inngest/functions/index.ts - Add workflow definition to database seed
// Example: New workflow function
export const myWorkflow = inngest.createFunction(
{
id: "my-workflow",
name: "My Workflow",
retries: 3,
},
{ event: "carehub/my.event" },
async ({ event, step }) => {
const runId = await step.run("create-run", async () => {
return await createWorkflowRun({
workflowSlug: "my-workflow",
inngestEventId: event.id,
caseId: event.data.caseId,
});
});
try {
// Workflow steps...
await completeWorkflowRun(runId);
} catch (error) {
await failWorkflowRun(runId, error.message);
throw error;
}
}
);
Testing Workflows
# Trigger event manually
curl -X POST http://localhost:8288/v1/e/carehub/intake.completed \
-H "Content-Type: application/json" \
-d '{"caseId": "...", "memberId": "..."}'