Solo build · API + Web · 4 months. In production with 2 therapists - one launching a course with 30+ simultaneous students. A psychologist wanting to work online shouldn't have to stitch together four tools and four logins. I built Germina Prime end-to-end for the client: a single branded platform with timezone-aware booking, video sessions that happen inside the product (1:1 therapy and group courses for 30+), a blog she publishes herself, and a store for e-books and cohort-based courses with card and boleto payments. Delivered complete and still evolving.
What it solves
A therapist going online usually ends up with Calendly for scheduling, Zoom for sessions, some marketplace for courses and a Wix site on top - four logins, four bills, four places for something to break, and a patient who has to install an app to be seen. Germina is one platform under her own brand where booking, the session, the blog and the store all live together, and the patient just clicks a link.
Video that does double duty
Sessions happen inside the platform, built on the Stream Video SDK (WebRTC) with role-based tokens minted server-side. The same infrastructure powers two very different modes: 1:1 therapy, where the therapist holds the admin token that allows screen share; and group courses, where one host runs a class for 30+ students who auto-mute camera and mic on join, with the host able to mute individuals and cleanly end the session for everyone.
Booking
Timezone-aware scheduling pinned to America/São_Paulo, therapist-configurable working hours and services with their own pricing, a working-hours index that prevents double-booking, and an in-session consultation timer that persists elapsed time onto the appointment.
The store, done properly
A generic store-item model serves e-books, single products and cohort-based courses. Each item can have enrollment batches (turmas) with their own capacity, open/close window and price; checkout runs through Stripe with card and boleto, a cron auto-expires stale pending orders, and payment status is pushed back to the buyer in real time over a dedicated WebSocket channel - so they see 'paid' the instant the webhook lands.
Built to last
Clean architecture (use-cases, repositories, domain models), migrations-first schema (no auto-sync in production), security hardening (Helmet, a CORS allowlist, bcrypt, XSS sanitization on user content) and graceful shutdown that drains the connection pool. Tested with Jest on the backend and Vitest on the frontend. Delivered complete for the client; I keep evolving it.
- Why build video into the platform instead of linking Zoom?In-platform WebRTCThe session is the product. Keeping it in-brand, with screen share for 1:1 and a managed group mode for courses, is the differentiator - and it removes the install friction that loses patients before the first call.
- Why the Stream Video SDK?Managed WebRTC + role tokensProduction-grade WebRTC with server-minted role tokens and real group calls, without running and scaling my own media servers. It let one infrastructure serve both 1:1 therapy and 30+ courses.
- Why a generic store-item + batches model?One model, many product typesE-books, single products and cohort courses all share one model, and a course's enrollment windows are just batches. Launching a new class is data, not a code change.
- Why support boleto, not just card?Card + boletoA large slice of Brazilians still pay for courses by boleto. Leaving it out quietly loses real sales, so checkout supports both with the same real-time payment-status flow.
In production seeing real patients, with a course launch for 30+ simultaneous participants (screen share included) in preparation. A complete product delivered for the client: booking, in-platform 1:1 and group video, a self-served blog and a card+boleto course store, on clean architecture I keep evolving.