The shipped pipeline
A React client uploads media and shows a live credit-cost preview before the user commits. A Fastify API stores the upload in MinIO and the job's metadata in PostgreSQL via Drizzle, then publishes a processing job onto a Redis Stream. A Python worker consumes it through a Redis consumer group — acknowledging on success, retrying on failure — so a crashed worker never silently drops a job someone already paid credits for.
OpenAI Whisper produces timestamped transcript segments.
Independent Gemini calls generate cited summaries, keywords, main ideas, and notes concurrently once transcription lands — they don't depend on each other, so they don't run sequentially.
Text-to-speech output is generated and uploaded to MinIO.
The client doesn't poll blindly — it tracks queued, processing, completed, and failed states, and synchronizes transcript timestamps with summary citations against the media player, so a citation is always a clickable jump to the exact moment it came from.
Designing for the failure case, not just the happy path
The part of this project I'd point to in an interview is the v2 credit settlement design — an accepted architecture decision (documented as an ADR) for a problem the v1 system only partially solved: users select several processing options up front, each with a credit cost, but processing can partially fail, retry, or get cancelled mid-flight. Charging on request creation is simple and wrong; charging on completion needs to survive retries and duplicate delivery without ever charging twice.
A versioned pricing catalog returns a quote for the selected options; the request stores that exact quote, so a later catalog price change never silently rewrites what an in-flight job owes.
One reservation for the quoted maximum moves credit from available to reserved, keyed by an idempotency key derived from account + workflow ID — a duplicate reservation command returns the existing reservation instead of double-reserving.
Each durable outcome (a completed transcript, a completed summary) settles its own price item exactly once. Retries, duplicate commands, and replayed completion events settle nothing extra.
Failed, cancelled, or skipped items release their reserved amount; when the workflow reaches a terminal state, any unsettled remainder is released automatically.
Why this shape
The alternative — deduct the full cost on request creation, refund on failure — is simpler to build and exactly where double-charge and lost-refund bugs come from under retries and at-least-once delivery. Modeling settlement as idempotent, per-outcome, ledger-append operations moves the correctness guarantee into the data model instead of relying on every call site to handle every failure path correctly by convention.