Inspiration

The first 10 minutes of every appointment go to one question: "How have you been?"

For patients with chronic conditions, rheumatoid arthritis, MS, chronic pain, cancer survivorship, that's not a small ask. They're trying to summarize weeks of fluctuating pain, fatigue, and medication side effects from memory, in real time, under pressure.

We kept hearing the same two complaints, from opposite sides of the exam table:

Patients: "I forget what happened by the time I get to my appointment." Doctors: "I spend so much time reconstructing timelines from incomplete information."

Existing tools don't close that gap. Symptom trackers collect data but don't synthesize it. EHRs like Epic summarize what's already in the chart, but the daily reality between visits never makes it into the record in the first place.

Interim closes that gap.

What it does

Interim is an AI-powered longitudinal briefing system with two sides: one for patients, one for doctors.

For patients, a voice-first check-in The patient taps a mic and talks naturally about how they're feeling. As they speak, our AI extracts structured clinical metrics in real time:

  • Pain level
  • Fatigue
  • Swelling
  • Medication adherence
  • Morning stiffness
  • Any custom parameters their doctor has assigned

A checklist highlights live as they talk. If something's missing, the AI asks one natural follow-up question, not a form, a conversation.

For doctors, a physician-ready briefing Before walking into the room, the doctor sees:

  • A "Why This Visit Matters" card with the key data points
  • A since-last-visit narrative
  • Metric trends over time
  • A pain chart with clinical events overlaid
  • A key events timeline
  • The patient's own words, verbatim

Doctors can regenerate the briefing as new check-ins come in, edit it before the visit, or flag a patient for follow-up. They can also assign custom tracked parameters per patient, for example a rheumatologist who wants morning stiffness tracked specifically, and every future check-in for that patient will listen for it.

How we built it

Stack: Full-stack TypeScript monorepo. Frontend on Vercel, backend on Render, Redis Cloud as the primary data store.

Patient check-in, two-phase architecture to kill perceived latency:

  1. While the patient is speaking, live Deepgram transcripts trigger instant keyword scanning. Checklist highlights in real time, no API call, no wait.
  2. Once they tap Done, a single Claude Haiku call extracts confirmed metrics from the final transcript. The ~1.5s wait happens after they're done talking, so it reads as intentional, not slow.

Physician briefing: A single Claude Sonnet call generates the briefing from the patient's full check-in history. History is compressed by The Token Company's wrapper before it hits the model, keeping prompts cheap and fast even as history accumulates. All Claude calls are traced through Arize AX with full prompt/response visibility and token counts before and after compression.

Anomaly detection: Runs automatically after every completed check-in, flagging:

  • Pain spikes ≥ 8/10
  • Rising trends over 3+ consecutive check-ins
  • Medication non-adherence across multiple recent check-ins
  • New symptoms appearing for the first time

Flagged patients get a warning badge in the doctor directory and a banner on their profile.

Challenges we ran into

localhost resolving to the wrong IP stack silently broke every request.

On one of our machines, localhost resolved to ::1 (IPv6), but Node was binding to 0.0.0.0 (IPv4). Frontend and backend looked correctly configured, but every request was getting refused with no useful error, just silence. We traced it to the bind address vs. resolution mismatch and fixed it by forcing 127.0.0.1 everywhere: Vite proxy target, API base URLs, curl tests.

Two separate startup bugs masqueraded as "nothing is happening."

The Token Company's SDK is ESM-only, but our backend started as CommonJS. Calling require() on an ESM package doesn't throw a clear error, it just fails silently, so the symptom looked like a missing dependency rather than a module system conflict. We fixed it with dynamic import() and lazy client initialization.

Around the same time, tsx watch would start the backend, print nothing, and never open its port, again with no crash and no log. The actual cause was a top-level await in our entrypoint blocking execution before the server ever bound to a port. Wrapping startup in an async function main() resolved it.

Accomplishments that we're proud of

Per-patient tracked parameters. Instead of one global extraction prompt for every patient, doctors choose exactly what to monitor, and the AI adjusts what it listens for in that patient's future check-ins. It's the difference between Interim feeling like a generic app and feeling like an actual clinical tool.

What we learned

Validate before you build. Our first instinct was to build a structured symptom-logging form, it felt easy to scope and easy to demo. But once we actually talked to patients and doctors, we kept hearing the same thing from both sides: people forget what happened by the time they get to an appointment, and doctors waste time reconstructing timelines from memory. That told us the real problem wasn't collecting data, it was making sense of it. Spending that hour talking to people before writing code completely changed our direction, and honestly it was time better spent than coding would have been.

Scope ruthlessly. Last-minute features are risk, not progress. We had a long list of ideas going in: a full conversational agent, EHR integrations, custom tracked parameters per patient. We cut almost all of it and focused on one thing working end to end: voice in, structured doctor briefing out. At some point we realized that adding a feature in the last hour doesn't make the project stronger, it just gives us one more thing that could break. We'd rather show up with something small that actually works than something big that's half-finished.

How we explained it mattered just as much as building it. Our first drafts of the project story led with the tech stack, Deepgram, Claude, Redis, compression. It was all true, but it didn't make anyone care. The version that actually worked started with the problem itself: a doctor trying to piece together weeks of symptoms in a 10-minute appointment. We learned that explaining why something matters takes just as much effort as building it.

Split work by strength, not just evenly. We put backend pipeline work and the doctor dashboard on separate branches so we could move at the same time without blocking each other. Frontend ended up taking longer than we expected, and once we noticed that, we shifted more time toward it instead of sticking to our original plan. Noticing early and adjusting saved us from a crunch later.

Things break exactly when you're testing the real flow. Almost all of our worst bugs, the IPv6 issue, the silent ESM failure, showed up when we were running the whole pipeline end to end, not during smaller tests. That taught us to always keep a version we knew worked, just in case something broke later while we were still building.

What's next for Interim

Right now, check-ins are single-turn: the patient talks, we extract, done. The next version moves to a full conversational agent that can hold a multi-turn dialogue, asking clarifying questions, following up on previous check-ins, and probing gently when something sounds off, instead of a one-shot speak-and-extract flow.

We also want to get Interim out of its own silo and into the tools doctors already use. That means partnering with platforms like MyChart and Epic so briefings show up directly in a doctor's existing workflow, instead of asking them to check a separate app before every visit.

Built With

Share this project:

Updates