# Project export: CareLink

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: Voice AI with RAG-powered clinical reasoning that proactively detects post-surgical complications, alerts care providers before symptoms escalate, and prevents costly hospital readmissions
- Devpost: https://devpost.com/software/carelink-k1pzeh
- GitHub: https://github.com/JoyZhuoz/carelink
- Video: https://www.youtube.com/embed/C60iv-taFpQ?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner ([Elastic] Best end-to-end Agentic system on Elasticsearch (1st Place: $2,000 (split across team members) 2nd Place: $1,000 (split across team members)))
- Team: 5 GitHub contributor(s) — mag-zhou (24 commits), Mingjia Zhang (11 commits), joyzhuo (10 commits), Claude Opus 4.6 (7 commits), Sanchali Banerjee (3 commits)

## Devpost submission (written by the team)

### Inspiration

1 in 5 Medicare recipients are readmitted within just a month of discharge from the hospital due to post-surgery complications - complications which would have been highly avoidable had proactive and sufficient follow-up been conducted on discharged patients. Readmissions cost the U.S. hospital system 26 billion dollars a year, exerting stress upon understaffed and busy hospital systems without the bandwidth to ensure patients get consistent care after walking through the doors of the operating room.

### What it does

CareLink aims to take the burden off of doctors, hospital systems, and even patients, by automating the post-surgical follow-up process while keeping medical staff in the loop - resulting in safe and efficient decision-making. The platform accomplishes two main purposes: 1) Automate patient post-surgical follow-up through agentic and adaptive voice call 2) Provide hospitals and doctors with digestible patient care recommendations through RAG-powered clinical reasoning. 48 hours after a patient’s discharge date, our voice AI agent automatically calls the patient for a check-in to ask the patient about any discomfort they’ve experienced and any concerns they have. With access to the patient’s surgery information, preexisting risk factors, and medical documentation on potential surgery complications, the agent dynamically reasons about follow-up questions to extract more information from the patient and determine the possibility of major surgery complications. If the agent believes the patient may be at risk, the hospital-facing dashboard flags the patient as requiring urgent care, alerting doctors to the need for follow-up appointments - preventing escalation of the patient condition and reducing the chances of patient readmission for increasingly severe surgical complications.

### How we built it

We built CareLink with React for the frontend, Express.js for the backend, and a demo patient database stored using Elasticsearch’s powerful vector embedding and retrieval capabilities. 1) Pre-call information retrieval: We use Perplexity API to gather contextual information from credible medical documentation (including PubMed, NCBI, and FDA publications) on the patient’s surgery and potential complications. Next, we use Elasticsearch’s Jina embedding model to convert this doc to embedding vectors for more efficient comparison with the patient’s true symptoms. 2) Automated phone call pipeline: We use the Twilio API to provide the communication infrastructure via a direct phone call 48 hours after the discharge date. We use a combination of Claude and ElevenLabs API to support dynamic, natural conversation with the patient. We leverage Claude's reasoning capabilities to generate personalized questions for the patient and use ElevenLabs for high quality text-to-speech conversion for the phone call. 3) Transcription and summarization: The agent-patient conversation is transcribed and summarized for clinician records. We compare the patient’s symptoms with the expected results retrieved using Perplexity via the vector evaluation system in Elasticsearch, allowing us to present the appropriate actionables in the hospital dashboard. 4) Hospital chatbot: On the hospital UI, we used Elastic Agent Builder via Kibana as part of Elastic Cloud. Specifically, we developed an agent that has access to customized workflows and tools to investigate specific data through semantic reasoning. The chatbot feature allows hospital clinicians to receive information about patients through retrieval-augmented generation that draws directly from patient records and call transcripts from the Elasticsearch database. 5) Clinician dashboards: We also incorporated an analytics dashboard to present various statistics about the patients and surgeries stored in the database. Finally, a full patient list allows clinicians to see individual patients data, call transcripts/summaries, and recommended actionables. In addition, they can manually initiate communication through phone call or email with the patient if needed.

### Challenges we ran into

1) TTS pipeline: The agent voice generated using the Twilio API for the full phone call pipeline was highly robotic. We integrated the ElevenLabs API for TTS to improve the flow of the agent’s speech; we definitely had to explore a lot of new tools to accomplish our goals for this project. 2) Performance obstacles: Our use of Elastic agents for the hospital chatbot resulted in timing bottlenecks due to the customized nature of the agent to focus on preplanning and RAG using patient information as opposed to immediate content synthesis. We restructured parts of our database pretty late into the hackathon in order to take advantage of faster ES|QL queries. 3) Multiple APIs: The integration of multiple APIs resulted in access and permission issues in the pipeline that required careful debugging to resolve. 4) Integration: Generally, connecting the various parts of the web app was difficult; we weren’t sure at first how to combine all the individual parts that everyone had worked on, but we were ultimately successful through a lot of trial and error.

### Accomplishments we're proud of

1) Successfully implementing an autonomous, dynamically reasoning clinical voice AI agent 2) Integrating dynamic reasoning abilities into agent-patient calls with context retrieval through Perplexity 3) Creating a chatbot using RAG for querying patient information and data 4) Managing and updating Elastic database and agents

### What we learned

We definitely learned a lot as a team. None of us had experience in designing and implementing agentic workflows, so diving into that process headfirst was very rewarding. We got to combine multiple state-of-the-art APIs and technologies to build an autonomous voice AI system for realistic patient follow-ups in healthcare, developing a full-stack agentic application using React and Express.js. We also began to think in a very product-design-oriented way, as we had to consider the very specific pain points of our target audience—like designing analytics dashboards with clinician UI/UX in mind and streamlining the patient and clinician experience in sensitive medical settings—which weren't necessarily pain points we'd have thought about before we started building. We found it incredibly cool to experiment with the voice AI and its capabilities; APIs are functional and applicable in ways we couldn’t have imagined, and seeing them work in action was really exciting.

### What's next

1) Semantic filtering in patient dashboard: Allow for more flexible and robust search of patient cards in the patient information tab 2) Computer vision integration: Incorporate CV to analyze images in patients’ medical reports and user-provided images of their symptoms 3) Dynamic monitoring: Initiate repeated follow-ups on a timeline personalized for each patient’s medical conditions and history 4) Performance improvements: Reduce latency of voice agent reasoning in real-time calls

## README (from the GitHub repository)


<p align="center">
  <img src="CareLink_Banner.png" alt="CareLink — Connecting Patients, Empowering Care" width="100%" />
</p>

***

<h2>CareLink: TreeHacks 2026 Winner of Elastic's Sponsor Challenge: Best End-to-End Agentic System on Elasticsearch (1st Place)</h2>

### Inspiration
1 in 5 Medicare recipients are readmitted within just a month of discharge from the hospital due to post-surgery complications - complications which would have been highly avoidable had proactive and sufficient follow-up been conducted on discharged patients. Readmissions cost the U.S. hospital system 26 billion dollars a year, exerting stress upon understaffed and busy hospital systems without the bandwidth to ensure patients get consistent care after walking through the doors of the operating room.

### What it does
CareLink aims to take the burden off of doctors, hospital systems, and even patients, by automating the post-surgical follow-up process while keeping medical staff in the loop - resulting in safe and efficient decision-making. The platform accomplishes two main purposes:

Automate patient post-surgical follow-up through agentic and adaptive voice call,
Provide hospitals and doctors with digestible patient care recommendations through RAG-powered clinical reasoning.

48 hours after a patient's discharge date, our voice AI agent automatically calls the patient for a check-in to ask the patient about any discomfort they've experienced and any concerns they have. With access to the patient's surgery information, preexisting risk factors, and medical documentation on potential surgery complications, the agent dynamically reasons about follow-up questions to extract more information from the patient and determine the possibility of major surgery complications.

If the agent believes the patient may be at risk, the hospital-facing dashboard flags the patient as requiring urgent care, alerting doctors to the need for follow-up appointments - preventing escalation of the patient condition and reducing the chances of patient readmission for increasingly severe surgical complications.

### How we built it
We built CareLink with React for the frontend, Express.js for the backend, and a demo patient database stored using Elasticsearch's powerful vector embedding and retrieval capabilities.

1. Pre-call information retrieval: We use Perplexity API to gather contextual information from credible medical documentation (including PubMed, NCBI, and FDA publications) on the patient's surgery and potential complications. Next, we use Elasticsearch's Jina embedding model to convert this doc to embedding vectors for more efficient comparison with the patient's true symptoms.

2. Automated phone call pipeline: We use the Twilio API to provide the communication infrastructure via a direct phone call 48 hours after the discharge date. We use a combination of Claude and ElevenLabs API to support dynamic, natural conversation with the patient. We leverage Claude's reasoning capabilities to generate personalized questions for the patient and use ElevenLabs for high quality text-to-speech conversion for the phone call.

3. Transcription and summarization: The agent-patient conversation is transcribed and summarized for clinician records. We compare the patient's symptoms with the expected results retrieved using Perplexity via the vector evaluation system in Elasticsearch, allowing us to present the appropriate actionables in the hospital dashboard.

4. Hospital chatbot: On the hospital UI, we used Elastic Agent Builder via Kibana as part of Elastic Cloud. Specifically, we developed an agent that has access to customized workflows and tools to investigate specific data through semantic reasoning. The chatbot feature allows hospital clinicians to receive information about patients through retrieval-augmented generation that draws directly from patient records and call transcripts from the Elasticsearch database.

5. Clinician dashboards: We also incorporated an analytics dashboard to present various statistics about the patients and surgeries stored in the database. Finally, a full patient list allows clinicians to see individual patients data, call transcripts/summaries, and recommended actionables. In addition, they can manually initiate communication through phone call or email with the patient if needed.


## Architecture

```
┌──────────────────────────────────────────────────────────┐
│                      PATIENT LAYER                       │
└──────────────────────────────────────────────────────────┘
                            ▲
                            │
┌──────────────────────────────────────────────────────────┐
│  Conversational Interface Layer                          │
│  - Web / Mobile App                                      │
│  - Text-to-speech (ElevenLabs)                           │
│  - Real-time streaming (Twilio)                          │
└──────────────────────────────────────────────────────────┘
                            ▲
                            │
┌──────────────────────────────────────────────────────────┐
│  Primary Clinical Agent                                  │
│  (Reasoning + Conversation Engine)                       │
│                                                          │
│  - Perplexity deep web research (PubMed, FDA, CDA)      │
│  - Jina API embedding similarity match                   │
│  - LLM (Elasticsearch-integrated Claude) reasoning       │
└──────────────────────────────────────────────────────────┘
                          │  ▲
                          ▼  │
┌──────────────────────────────────────────────────────────┐
│  Data & Retrieval Layer                                  │
│                                                          │
│  - Elastic Cloud database                                │
│  - Jina embedding for semantic search                    │
│  - Interactive clinician decision interface              │
└──────────────────────────────────────────────────────────┘
                          │  ▲
                          ▼  │
┌──────────────────────────────────────────────────────────┐
│  Agent Orchestration Layer                               │
│  (Multi-Agent Router + Controller)                       │
│                                                          │
│  - Elastic Agent Builder + Workflow Orchestrator         │
│  - Guardrails & Safety Policies                          │
│  - Escalation Rules + Clinical guidelines RAG            │
└────┬────────────┬────────────┬────────────┬──────────────┘
     ▼            ▼            ▼            ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Symptom  │ │ Action   │ │Escalation│ │ General  │
│ Tool     │ │ Tool     │ │ Tool     │ │ Reasoning│
│(Triage)  │ │(Follow-up│ │(Risk     │ │(FAQ /    │
│          │ │ Logic)   │ │Detector) │ │ Support) │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
     ▼            ▼            ▼            ▼
┌──────────────────────────────────────────────────────────┐
│  Clinical Intelligence Layer                             │
│                                                          │
│  - Medical NLP Model                                     │
│  - Structured clinical reasoning prompts                 │
│  - Interactive decision support interface                │
└──────────────────────────────────────────────────────────┘

```

## Project Structure

```
carelink/
├── client/                      # React frontend (Vite + Tailwind)
│   └── src/
│       └── components/
│           ├── layouts/         # SidebarLayout (shared shell)
│           ├── modules/         # PatientCard, PatientProfile, CallSummary, etc.
│           ├── pages/           # Dashboard, Chatbot, Analytics
│           └── utils/           # Shared helpers (patientUtils)
├── server/                      # Express server
│   ├── server.js                # Main entry — routes, SSE chat, Socket.IO
│   └── services/
│       ├── callAgent.js         

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 84 recognized source files, 365 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- AI coding agent: Claude Code — evidence: commit authorship or trailers
- AI coding agent: Cursor — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (96 of 96)

```
.env.example
.gitignore
.npmrc
.prettierrc
client/index.html
client/src/client-socket.js
client/src/components/App.jsx
client/src/components/layouts/SidebarLayout.jsx
client/src/components/MainLayout.css
client/src/components/MainLayout.jsx
client/src/components/modules/.gitkeep
client/src/components/modules/BottomNav.css
client/src/components/modules/BottomNav.jsx
client/src/components/modules/CallSummary.jsx
client/src/components/modules/CallTranscript.jsx
client/src/components/modules/PatientCard.jsx
client/src/components/modules/PatientCards.jsx
client/src/components/modules/PatientProfile.jsx
client/src/components/modules/SearchBar.jsx
client/src/components/modules/Sidebar.jsx
client/src/components/modules/TabSwitcher.jsx
client/src/components/pages/Analytics.css
client/src/components/pages/Analytics.jsx
client/src/components/pages/Chatbot.jsx
client/src/components/pages/Dashboard.css
client/src/components/pages/Dashboard.jsx
client/src/components/pages/DashboardTab.css
client/src/components/pages/DashboardTab.jsx
client/src/components/pages/Home.css
client/src/components/pages/Home.jsx
client/src/components/pages/NotFound.jsx
client/src/components/pages/Skeleton.css
client/src/components/pages/Skeleton.jsx
client/src/components/utils/patientUtils.js
client/src/index.jsx
client/src/public/.gitkeep
client/src/utilities.css
client/src/utilities.js
data/patients.json
data/patients.ndjson
docs/agent_response.md
docs/agent_setup.md
docs/elastic_search_data.md
docs/joy_README.md
LICENSE
package.json
patient-followup/config/agent-builder-config.js
patient-followup/config/elasticsearch.js
patient-followup/docs/AGENT_BUILDER_SETUP.md
patient-followup/package.json
patient-followup/routes/analytics.js
patient-followup/routes/patients.js
patient-followup/routes/twilio.js
patient-followup/scripts/backfill-current-triage.js
patient-followup/scripts/seed-10-multi-call-patients.js
patient-followup/scripts/seed-20-patients.js
patient-followup/scripts/seed-50-patients.js
patient-followup/scripts/seed-patient-due-now.js
patient-followup/scripts/seed-patient-jeff-bezos.js
patient-followup/scripts/seed-patient-joy.js
patient-followup/scripts/seed-patient-steven-jobs.js
patient-followup/scripts/seed-patients.js
patient-followup/services/analyticsService.js
patient-followup/services/claudeService.js
patient-followup/services/elevenLabsService.js
patient-followup/services/embeddingService.js
patient-followup/services/patientService.js
patient-followup/services/perplexityService.js
patient-followup/services/schedulerService.js
patient-followup/services/twilioService.js
postcss.config.js
README.md
scripts/test-twilio-e2e.js
server/.env.example
server/api.js
server/config/elasticsearch.js
server/prompts/agentBuilderPrompt.js
server/routes/admin.js
server/routes/patients.js
server/scripts/ingestJSON.js
server/scripts/ingestPDF.js
server/scripts/inspectES.js
server/server-socket.js
server/server.js
server/services/callAgent.js
server/services/chatFallback.js
server/services/elasticService.js
server/services/emailService.js
server/setup/createAgent.js
server/setup/createIndices.js
server/twilio.js
server/validator.js
SETUP.md
tailwind.config.js
tests/phase1.test.js
vite.config.js
```

### Dependencies

- package.json: @anthropic-ai/sdk@^0.39.0, @elastic/elasticsearch@^9.3.0, @vitejs/plugin-react-swc@^3.0.0, autoprefixer@^10.4.24, axios@^1.13.5, cloudflared@^0.7.1, core-js@^3.8.1, dotenv@^16.0.3, express@^4.17.1, node-cron@^4.2.1, nodemailer@^6.10.1, nodemon@^3.1.9, pdf-parse@^2.4.5, postcss@^8.5.6, react@^18.2.0, react-dom@^18.2.0, react-markdown@^10.1.0, react-router@^5.3.4, react-router-dom@^6.0.0, recharts@^3.7.0, remark-gfm@^4.0.0, socket.io@^4.8.1, socket.io-client@^4.8.1, tailwindcss@^3.4.19, twilio@^5.12.1, url-loader@^4.1.1, vite@^4.0.0, vite-plugin-svgr@^2.4.0
- patient-followup/package.json: @anthropic-ai/sdk@^0.74.0, @elastic/elasticsearch@^8.x.x, axios@^1.13.5, dotenv@^16.x.x, express@^4.x.x, node-cron@^4.2.1, twilio@^5.12.1

### Recent commits (newest first)

- Add CareLink's TreeHacks 2026 achievement to README
- Update README.md
- Update README.md
- Add CareLink banner to README
- Chatbot action buttons always show both options, limit to 5 patients; reorganize docs
- no ring around avatar
- merged patientcardfix
- email functionality
- merge with new triage field
- new triage field
- added contact info to patient card
- Merge branches 'main' and 'main' of https://github.com/JoyZhuoz/carelink
- json changewS
- Upgraded analytics UI and added surgery recovery visualizations
- Updated agent setup
- chatbot UI polish: colors, pt-ID hover cards, dummy action buttons
- chatbot hover cards, action buttons, and patient update endpoints
- Fixed symptom/surgery analytics graph and text overlap
- fixed bugs
- added stock photos for pfp

## Key source files (fetched from GitHub, selected and truncated for size)

### SETUP.md

```markdown
# Setup Guide

## Prerequisites

- **Node.js** >= 20.x
- **npm** (comes with Node.js)
- **Elasticsearch** deployment on Elastic Cloud (with a `patients` index)
- **Kibana** with Agent Builder enabled (for the clinical chatbot)
- **Twilio** account (for automated patient calls)
- **Cloudflared** (optional, for exposing local server to Twilio webhooks)
- **ElevenLabs** API key (optional, for natural voice — falls back to Twilio's built-in "alice" voice)

> **Note:** This is a Node.js project. There is no `requirements.txt` — all dependencies are managed through `package.json` via `npm install`.

## Installation

### 1. Clone and install

```bash
git clone https://github.com/JoyZhuoz/carelink.git
cd carelink
npm install
```

If you get permission errors on Windows:
- Close VS Code/Cursor completely
- Disable any antivirus temporarily
- Run Command Prompt as Administrator and navigate to the project folder
- Try: `npm install --legacy-peer-deps`

### 2. Configure environment variables

```bash
cp .env.example .env
```

Edit `.env` and fill in your credentials:

| Variable | Required | Description |
|---|---|---|
| `TWILIO_ACCOUNT_SID` | Yes | Twilio Account SID |
| `TWILIO_AUTH_TOKEN` | Yes | Twilio Auth Token |
| `TWILIO_PHONE_NUMBER` | Yes | Your Twilio phone number (E.164 format) |
| `PUBLIC_BASE_URL` | Yes | Public URL for Twilio webhooks (use tunnel URL in dev) |
| `ELASTICSEARCH_URL` | Yes | Elasticsearch deployment URL |
| `ELASTICSEARCH_API_KEY` | Yes | Elasticsearch API key |
| `KIBANA` | Yes | Kibana base URL (for Agent Builder chat) |
| `ES_CHAT_INFERENCE_ID` | No | ES inference endpoint ID for LLM chat completion |
| `ES_AGENT_BUILDER_ENDPOINT` | No | Agent Builder run endpoint |
| `ES_AGENT_BUILDER_API_KEY` | No | Agent Builder API key |
| `ELEVENLABS_API_KEY` | No | ElevenLabs API key for natural voice |
| `ELEVENLABS_VOICE_ID` | No | ElevenLabs voice ID |

### 3. Seed patient data (optional)

```bash
npm run seed
```

Seeds a test patient into Elasticsearch for development.

### 4. Set up Twilio webhook tunnel (for local development)

Twilio needs a public URL to send call webhooks back to your server. In a separate terminal:

```bash
npm run tunnel
```

Copy the generated URL and set it as `PUBLIC_BASE_URL` in your `.env`.

### 5. Set up the Elastic Agent Builder

See [`data/agent_setup.md`](data/agent_setup.md) for the full agent instruction and ES|QL workflow configuration.

## Running

Start the backend and frontend dev server in two terminals:

```bash
# Terminal 1 — Backend (Express + Socket.IO on port 3000)
npm start

# Terminal 2 — Frontend (Vite dev server with HMR on port 5173)
npm run dev
```

For production, build the frontend first:

```bash
npm run build
npm start
```

The server serves the built frontend from `client/dist` and runs on port 3000.

## Available Scripts

| Command | Description |
|---|---|
| `npm start` | Start the production server on port 3000 |
| `npm run dev` | Start Vite dev server with HMR |
| `npm ru
[truncated — 771 more characters]
```

### docs/joy_README.md

```markdown
❯ node tests/test-agent-query.js
AI mode: direct_claude
>>> What is John Smith's current recovery status and what questions should I ask on the next check-in call?

=== Response ===

Based on John Smith's records, here's his current recovery status and recommended check-in approach:

## Current Recovery Status (Post-Op Day 3)

**Overall:** Stable and progressing as expected for ACL reconstruction

**Key Findings:**
- Pain is moderate (6/10 standing, 3/10 at rest) - within normal range
- Mild swelling present but being managed with ice/elevation
- Incision appears clean with no drainage or signs of infection
- No fever, chills, or concerning symptoms
- Patient is compliant with medications and ankle pumps
- Using brace appropriately

**Risk Considerations:**
- Diabetes + obesity increase infection and DVT risk
- Requires close monitoring of wound healing
- Enhanced focus on mobility/circulation exercises

## Recommended Check-in Questions for Next Call

Given his risk factors and current status, prioritize these questions:

1. **Pain Assessment:** "How is your pain today on a scale of 1 to 10, both at rest and when you're moving around?"

2. **Wound/Infection Monitoring:** "Can you describe how your incision looks today? Any redness, warmth, drainage, or changes since yesterday?"

3. **DVT Screening:** "Have you noticed any pain, swelling, or warmth in your calf or lower leg? How often are you doing your ankle pumps?"

4. **Mobility Progress:** "Have you been able to take short walks as instructed? How is your knee bending coming along?"

5. **Systemic Symptoms:** "Any fever, chills, or feeling unwell overall?"

**Focus Areas:** Given his diabetes and obesity, spend extra time on wound appearance and DVT symptoms. Reinforce the importance of daily skin checks and maintaining movement within prescribed limits.
```

### package.json

```
{
  "name": "carelink",
  "version": "1.0.0",
  "type": "module",
  "description": "CareLink – AI-powered post-surgical readmission prevention",
  "main": "server/server.js",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "start": "node server/server.js",
    "start:dev": "nodemon server/server.js",
    "tunnel": "npx cloudflared tunnel --url http://localhost:3000",
    "seed": "node patient-followup/scripts/seed-patient-due-now.js",
    "seed:steven": "node patient-followup/scripts/seed-patient-steven-jobs.js",
    "seed:20": "node patient-followup/scripts/seed-20-patients.js",
    "seed:10multi": "node patient-followup/scripts/seed-10-multi-call-patients.js",
    "seed:50": "node patient-followup/scripts/seed-50-patients.js",
    "test:call": "curl -s -X POST http://localhost:3000/api/twilio/call/seed-due-now | python3 -m json.tool"
  },
  "engines": {
    "node": ">=20.x"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.39.0",
    "@elastic/elasticsearch": "^9.3.0",
    "axios": "^1.13.5",
    "core-js": "^3.8.1",
    "dotenv": "^16.0.3",
    "express": "^4.17.1",
    "node-cron": "^4.2.1",
    "nodemailer": "^6.10.1",
    "nodemon": "^3.1.9",
    "pdf-parse": "^2.4.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-markdown": "^10.1.0",
    "react-router": "^5.3.4",
    "react-router-dom": "^6.0.0",
    "recharts": "^3.7.0",
    "remark-gfm": "^4.0.0",
    "socket.io": "^4.8.1",
    "socket.io-client": "^4.8.1",
    "twilio": "^5.12.1",
    "url-loader": "^4.1.1"
  },
  "devDependencies": {
    "@vitejs/plugin-react-swc": "^3.0.0",
    "autoprefixer": "^10.4.24",
    "cloudflared": "^0.7.1",
    "postcss": "^8.5.6",
    "tailwindcss": "^3.4.19",
    "vite": "^4.0.0",
    "vite-plugin-svgr": "^2.4.0"
  }
}

```

### patient-followup/package.json

```
{
    "name": "patient-followup",
    "version": "1.0.0",
    "type": "module",
    "main": "server.js",
    "scripts": {
        "start": "node server.js"
    },
    "dependencies": {
        "@anthropic-ai/sdk": "^0.74.0",
        "@elastic/elasticsearch": "^8.x.x",
        "axios": "^1.13.5",
        "dotenv": "^16.x.x",
        "express": "^4.x.x",
        "node-cron": "^4.2.1",
        "twilio": "^5.12.1"
    }
}

```

### server/server.js

```javascript
/**
 * ============================================================================
 * CareLink – Unified Server
 * ============================================================================
 *
 * Single Express server that:
 *   1. Serves the React frontend (client/dist)
 *   2. Mounts patient-followup API routes (patients, twilio, scheduler)
 *   3. Provides a Socket.IO layer for real-time dashboard updates
 *   4. Starts the daily follow-up scheduler
 *   5. Exposes /api/chat for the Agent Builder chatbot
 */

import path from "path";
import { fileURLToPath } from "url";
import dotenv from "dotenv";
import http from "http";
import express from "express";
import { Server as SocketIOServer } from "socket.io";

// ── Load .env (server/.env first, then root .env) ─────────────────────────────
const __dirname = path.dirname(fileURLToPath(import.meta.url));
dotenv.config({ path: path.join(__dirname, ".env") });
dotenv.config({ path: path.join(__dirname, "..", ".env") });
dotenv.config();

// ── Patient-followup imports (ESM) ───────────────────────────────────────────
import patientRoutes from "../patient-followup/routes/patients.js";
import twilioRoutes from "../patient-followup/routes/twilio.js";
import analyticsRoutes from "../patient-followup/routes/analytics.js";
import { startScheduler, runFollowUpNow } from "../patient-followup/services/schedulerService.js";

// ── Chat: Kibana Agent Builder only ─────────────────────────────────────────
import * as callAgent from "./services/callAgent.js";
import { sendEmail, isEmailConfigured } from "./services/emailService.js";

// ── Express app ──────────────────────────────────────────────────────────────
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

// Request logger (helpful for debugging webhooks)
app.use((req, _res, next) => {
  console.log(`[REQ] ${req.method} ${req.url}`);
  next();
});

// ── API routes ───────────────────────────────────────────────────────────────
app.use("/api/patients", patientRoutes);
app.use("/api/twilio", twilioRoutes);
app.use("/api/analytics", analyticsRoutes);

// ── Chat route (Kibana Agent Builder only) ───────────────────────────────────
app.post("/api/chat", async (req, res) => {
  try {
    const { message, conversation_id } = req.body;
    if (!message) {
      return res.status(400).json({ error: "Provide 'message'" });
    }
    if (!callAgent.isConfigured()) {
      return res.status(503).json({
        error: "Chat not configured. Set KIBANA and ELASTICSEARCH_API_KEY in server/.env.",
      });
    }
    const result = await callAgent.converse(message, conversation_id);
    res.json({ conversation_id: result.conversation_id, response: result.response });
  } catch (err) {
    console.error("Chat error:", err);
    res.status(500).json({ error: err.message });
  }
});

// Send email (e.g. from PatientCard)
const EMAIL_TO = "margo.joe708@gmail.com";
app.post("/api/email/send", async (req, res) => {
  try {
    if (!isEmailConfigured()) {
      return res.status(503).json({ error: "Email not configured. Set EMAIL_USER and EMAIL_APP_PASSWORD in server/.env." });
    }
    const { to, subject, text } = req.body;
    await sendEmail({
      to: to || EMAIL_TO,
      subject: subject || "(No subject)",
      text: text || "",
    });
    res.json({ ok: true, message: "Email sent" });
  } catch (err) {
    console.error("Email send error:", err);
    res.status(500).json({ error: err.message });
  }
});

// Manual follow-up trigger
app.post("/api/run-followup", async (_req, res) => {
  try {
    const results = await runFollowUpNow();
    res.json({ message: "Follow-up completed", results });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Health check
app.get("/api/health", (_req, res) => res.json({ status: "ok" }));
app.get("/health", (_req, res) => res.json({ status: "ok" }));

// ── Static React frontend ────────────────────────────────────────────────────
const reactPath = path.resolve(__dirname, "..", "client", "dist");
app.use(express.static(reactPath));

// SPA fallback – only for non-API GET (so /api/* never gets HTML)
app.get("*", (req, res, next) => {
  if (req.path.startsWith("/api")) return next();
  res.sendFile(path.join(reactPath, "index.html"), (err) => {
    if (err) {
      res.status(err.status || 500).send("Frontend not built yet – run `npm run build` first.");
    }
  });
});
// 404 for unmatched API routes
app.use((req, res) => {
  if (req.path.startsWith("/api")) return res.status(404).json({ error: "Not found" });
  res.status(404).send("Not found");
});

// ── Error handler (TwiML for Twilio so they never see JSON → "application error") ──
const errorTwiml = () =>
  '<?xml version="1.0" encoding="UTF-8"?><Response><Say voice="alice">We\'re sorry, something went wrong. Goodbye.</Say><Hangup/></Response>';

app.use((err, req, res, _next) => {
  console.error("Server error:", err?.message || err);
  if (err?.stack) console.error(err.stack);
  const status = err.status || 500;
  if (req.path.startsWith("/api/twilio") && req.method === "POST") {
    res.type("text/xml").status(status).send(errorTwiml());
    return;
  }
  res.status(status).json({ error: err.message });
});

// ── Start server + Socket.IO ─────────────────────────────────────────────────
const PORT = process.env.PORT || 3000;
const server = http.createServer(app);

const io = new SocketIOServer(server, {
  cors: { origin: "*" },
});

io.on("connection", (socket) => {
  console.log("Dashboard connected:", socket.id);
  socket.on("disconnect", () => console.log("Dashboard disconnected:", socket.id));
});

// Export io so services can emit events (e.g. new triage result)
export { io };

server.listen(PORT, () => {
  console.log(`CareLink server running on port ${PORT}`);
  startScheduler();
});

```

### client/src/index.jsx

```javascript
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./components/App";
import SidebarLayout from "./components/layouts/SidebarLayout";
import Dashboard from "./components/pages/Dashboard";
import Analytics from "./components/pages/Analytics";
import Chatbot from "./components/pages/Chatbot";
import NotFound from "./components/pages/NotFound";

import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
  RouterProvider
} from 'react-router-dom'

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route element={<App />} errorElement={<NotFound />}>
      {/* All main pages share the sidebar layout */}
      <Route element={<SidebarLayout />}>
        <Route path="/" element={<Dashboard />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/analytics" element={<Analytics />} />
        <Route path="/chatbot" element={<Chatbot />} />
      </Route>
      <Route path="*" element={<NotFound />} />
    </Route>
  )
)

ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);

```

### client/src/components/App.jsx

```javascript
import React from "react";
import { Outlet } from "react-router-dom";

import "../utilities.css";

/**
 * Define the "App" component
 */
const App = () => {
  return <Outlet />;
};

export default App;

```

### postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./client/index.html",
    "./client/src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {
      colors: {
        'coral': {
          500: '#FA8072',
          600: '#F67868',
        },
        'peach': '#FFEAA7',
        'mint': '#98D8C8',
      },
    },
  },
  plugins: [],
}

```

### vite.config.js

```javascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import svgr from 'vite-plugin-svgr';
import path from 'path';

export default defineConfig({
  plugins: [react(), svgr()],
  root: path.resolve(__dirname, 'client'), // Set the root directory for Vite
  build: {
    outDir: path.resolve(__dirname, 'client/dist'), // Output directory for production build
  },
  server: {
    port: 5173,
    proxy: {
      '/api': {
        target: "http://localhost:3000",
        changeOrigin: true,
      },
      '/socket.io': {
        target: 'http://localhost:3000',
        ws: true,
        changeOrigin: true,
    },
    }
  }
});
```

[74 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]