Backend¶
Internal
This page describes implementation details for the GiveCare development team.
GiveCare's backend runs on Convex — a reactive database with built-in serverless functions1. The backend manages users, messages, assessments, eligibility, loops, and safety flags.
Core tables¶
| Table | Purpose | Key fields |
|---|---|---|
users |
Caregiver profiles | phone, name, state, consent status, bootstrap stage, current loop |
messages |
SMS conversation history | userId, direction (inbound/outbound), content, timestamp, loop, riskLevel |
assessments |
Instrument responses and scores | userId, instrumentId, responses, zoneScores, compositeScore, confidence |
eligibility |
Benefits matching results | userId, programId, eligible (boolean), matchedZones, surfacedAt, status |
loops |
Conversational loop state | userId, loopName, stage, enteredAt, exitedAt, metadata |
safety_flags |
Crisis detection records | userId, tier (distress/imminent/continuity), triggeredAt, resolvedAt, reason |
Schema relationships¶
erDiagram
users ||--o{ messages : "sends/receives"
users ||--o{ assessments : "completes"
users ||--o{ eligibility : "matched to"
users ||--o{ loops : "participates in"
users ||--o{ safety_flags : "may trigger"
assessments }o--|| users : "scored for"
eligibility }o--|| users : "checked for"
Data agent services¶
The backend exposes services consumed by the SMS pipeline and scheduler:
Message handling¶
ingestMessage— Receives inbound SMS, runs risk assessment, stores message, returns risk levelsendMessage— Validates outbound message, stores in history, dispatches via SMS providergetConversationHistory— Returns recent messages for a user (used in prompt assembly)
Assessment services¶
submitAssessment— Stores instrument responses, computes zone scores, updates composite GiveCare ScoregetLatestScores— Returns most recent zone scores and composite for a userdetectSpike— Compares current score to previous, returns spike type if threshold exceeded
Benefits services¶
checkEligibility— Evaluates eligibility rules against user profile for a set of programssurfaceBenefit— Records that a program was presented to the userupdateBenefitStatus— Tracks application progress (surfaced, applied, approved, denied)
Loop management¶
getCurrentLoop— Returns the user's active loop and stagetransitionLoop— Moves user to a new loop, recording exit from previousadvanceStage— Moves user to the next stage within their current loop
Safety services¶
createSafetyFlag— Records a new safety flag with tier and trigger contextresolveSafetyFlag— Marks a flag as resolved with reasongetActiveSafetyFlags— Returns unresolved flags for a user
Scheduler¶
The scheduler drives proactive outreach. It runs on a periodic tick (configurable interval, default 15 minutes) and determines which users need attention.
Tick flow¶
flowchart TD
A["Scheduler tick"] --> B["List eligible users"]
B --> C["For each user: arbiter routing"]
C --> D{"Action needed?"}
D -->|Yes| E["Select loop + objective"]
D -->|No| F["Skip"]
E --> G["Execute turn"]
G --> H["Send outbound SMS"]
Eligibility filters¶
The scheduler evaluates each user against:
| Filter | Condition |
|---|---|
| Consent | Must have active consent |
| Quiet hours | Respect user's timezone, no messages during sleeping hours |
| Rate limit | Maximum messages per day per user |
| Active safety flag | If imminent tier active, route to continuity loop |
| Pending assessment | If scheduled assessment due, route to assessment loop |
| Spike detection | If score change exceeds threshold, route to proactive loop |
| Benefit follow-up | If surfaced benefit has no status update, route to benefits loop |
Arbiter routing¶
The arbiter is the decision layer that determines which loop a user enters on each tick. Priority ordering:
- Safety — Active safety flags always take precedence
- Assessment — Scheduled instruments that are due
- Benefits — Flagged zones with unsurfaced programs
- Proactive — General check-ins and follow-ups
Convex-specific patterns¶
Reactive queries¶
Convex queries are reactive — the admin dashboard and diagnostic tools subscribe to live data. When a safety flag is created, the dashboard updates in real time without polling.
Mutations and actions¶
- Mutations are deterministic database writes (message storage, flag creation, loop transitions)
- Actions are non-deterministic operations (calling the language model, sending SMS via provider, running risk assessment)
The SMS turn is an action that calls mutations internally — it reads state, calls the model, and writes the result back to the database.
Codegen¶
Convex types are generated from the schema definition. Run pnpm --filter @givecare/data-agent codegen to regenerate types after schema changes. The _generated/ directory is gitignored.
Code reference¶
Backend source in apps/data-agent/convex/. Key files:
| Path | Purpose |
|---|---|
schema.ts |
Table definitions |
scheduler/tick.ts |
Scheduler tick implementation |
loops/scheduledDispatcher.ts |
Arbiter routing logic |
sms/ingest.ts |
Inbound message handling |
sms/send.ts |
Outbound message dispatch |
assessments/ |
Instrument scoring and storage |
safety/ |
Crisis detection and flag management |
benefits/ |
Eligibility checking and surfacing |
See the SMS Journey for the full message flow and Crisis Routing for safety tier details.