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 values
  • NUMBER - Numeric thresholds
  • BOOLEAN - Feature flags
  • JSON - Complex configuration
  • PROMPT - 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

  1. Summarize Session - AI-generated summary from notes/transcript
  2. Update Progress - Record progress toward care plan goals
  3. Generate PR-2 - Auto-generate progress report document
  4. 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

  1. Email (primary) - If configured
  2. 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

  1. Get Session - Retrieve session details from database
  2. Wait for Recordings (30s) - Twilio needs time to process
  3. Fetch Recordings - Get download URLs from Twilio API
  4. Transcribe - Azure Speech Services batch transcription
    • Poll every 15s for up to 10 minutes
    • Format transcript with speaker labels
  5. Update Session - Store transcript, update status
  6. 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

  1. Check Sessions - Verify remaining session count
  2. Notify Provider - Alert treating provider via email/in-app
  3. 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

  1. Confirm Assessment - Validate risk indicators
  2. Notify Care Team - Alert coordinator and supervisor
  3. 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

  1. Process Check-in - Store mood, pain, sleep data
  2. Analyze Trends - Compare to previous check-ins
  3. 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
Email 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:

  1. Retry Logic - Inngest automatic retries (default 3 attempts)
  2. Error Recording - Failed runs stored with error details
  3. Step Isolation - Failures don't affect completed steps
  4. 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:

  1. Send notification email to assigned staff
  2. Send SMS if enabled
  3. Log activity

No-Show Follow-up

Trigger: Session marked as No-Show

Steps:

  1. Alert provider immediately
  2. Schedule delayed outreach (2 hours)
  3. Check for no-show pattern (3+ in 30 days)
  4. Flag compliance concern if pattern detected

Discharge Workflow

Trigger: Case status changed to Discharged

Steps:

  1. Deactivate member portal access
  2. Generate discharge documents (Treatment Summary, PR-4)
  3. Send member notification
  4. Notify coordinator
  5. 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

  1. Define event type in src/inngest/client.ts
  2. Create function file in src/inngest/functions/
  3. Export from src/inngest/functions/index.ts
  4. 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": "..."}'