Skip to content
Carlos Morais.
Work

Plumaa

A mental health platform for people, psychologists and companies

I joined when Plumaa was still just an idea. I picked the stack, designed the database and built the backend of a mental health platform with three audiences: people looking for therapy, psychologists and companies.

In 30 seconds

  • The only backend developer since the idea stage: I picked the stack (NestJS, Prisma, PostgreSQL) and designed the database.
  • Five roles with permissions decided in the backend, Stripe payments, Google scheduling and an AI pipeline for session notes.
  • In production, with ~100 active accounts; the AI summary saves each psychologist 3 to 8 hours a week.
When
Jan 2026 — present
Role
Full-Stack Developer · sole backend developer
Team
up to 3 developers
Stack
React (Vite), NestJS, Prisma, PostgreSQL, Graphile Worker, Stripe, Google APIs, Claude, Deepgram

Context

Plumaa is a mental health platform. People buy single sessions or plans to see partner psychologists; psychologists manage their schedule and sessions; and companies use a separate product to map psychosocial risks, as required by Brazil's NR-1 workplace regulation.

It's a small startup: the tech team peaked at three people. Today there are about 100 active accounts across clients, psychologists, companies and their employees.

The problem

A single backend had to serve five profiles with very different views of the same data, and some of that data is health data. A psychologist can't see another's data, a company can't see its employees' individual answers, and no one but the admin can get near the rest.

On top of that: billing for sessions and subscriptions, scheduling across time zones, and psychologists spending hours a week writing session notes by hand. All with a tiny team, at startup speed.

My part

I was the only backend developer. I chose the stack, designed the database and the access and storage strategy, and helped build the team by running the technical interviews. The other developers owned the frontend; I reviewed their work on staging and handed over every API change with its Swagger docs.

  • Access control for the five profiles: client, psychologist, company, employee and admin.
  • Stripe payments: single sessions, plans with benefits and company subscriptions.
  • Scheduling with Google Calendar and sign-in with Google.
  • The NR-1 B2B product: a questionnaire shared by public link with access checks, and the risk scoring in the database.
  • An AI pipeline that transcribes the session and drafts the clinical notes.
  • A system event log (session started, transcription started, payment made) for the admin dashboard.
  • Unit and integration tests covering every backend route and service.
  • Infrastructure: backend and database on Digital Ocean, frontend on Vercel, staging mirroring production.

Technical decisions

Permissions always on the backend

The session cookie says who the user is and which profile they have. Before any query reaches the repository, a layer decides which tables and columns that profile may read. Anything outside it returns 404, without even confirming the resource exists.

Postgres for everything, no Redis

Background jobs run on Graphile Worker, a queue stored in PostgreSQL itself. At today's load, that's one less service to pay for, monitor and keep running.

Stripe Checkout and idempotent webhooks

Payment happens on Stripe's page, so card data never touches our server. Access is only granted when the webhook arrives, and every transaction is recorded: if the same event arrives twice, it's ignored.

Schedules in UTC

Every time slot is stored in UTC and converted to the psychologist's working time zone. Booking is an attempt: the slot only moves on to payment if it's still free.

With Google, two scopes: everyone uses sign-in, but only psychologists grant calendar access.

Async, anonymized AI

When a session ends, a job takes over: it transcribes with Deepgram, strips out the personal data the database already knows about and sends the text to Claude to structure the notes. The psychologist gets a draft instead of a blank page.

Small, but ready to grow

One machine per service, on the simplest plan, because demand didn't call for more. The database only accepts connections from the backend and is backed up daily, and staging replicates the whole ecosystem from the development branch.

Architecture

The four dashboards talk to a single NestJS API, which decides from the session's profile what each one may see. The API keeps data, events and the job queue in PostgreSQL and talks to Stripe, Google and the AI services.

Outcome

The platform is in production, with about 100 active accounts across the three audiences. The AI session summary saves each psychologist who uses it 3 to 8 hours a week, time that used to go into writing notes from memory.

The boleto bug

For the subscriptions feature we spent days testing on staging: credit, debit, declined, expired, insufficient funds, timeouts. We shipped, it worked. Then a customer clicked “pay with boleto” (a Brazilian bank slip): someone had turned that option on directly in the Stripe dashboard, and the version we tested didn't have it.

We reverted the setting and helped the customer finish paying. The lesson: there will always be a scenario you didn't test. The goal isn't zero bugs, it's zero impact.