Wavess GTM platform
A multi-tenant SaaS platform that connects market signals to LinkedIn content and GTM actions for small B2B teams.
- Role
- Software engineering intern: analysis, architecture, full-stack build
- Period
- December 2025 to May 2026
- Status
- Delivered; end-of-studies report defended
- Stack
- Python, FastAPI, Supabase, pgvector, ARQ, Redis, HTMX, Groq, Gemini, Cerebras, NVIDIA NIM, Apify, Tavily, Fly.io
The problem
Wavess is a Berlin B2B SaaS startup. Its customers are small B2B companies in Germany, the UK and the Czech Republic that want market signals to turn into LinkedIn content and sales actions.
The gap it works on: market intelligence and content execution live in different tools. Teams carry insights from one to the other by hand, so signals rarely become timely action, and LinkedIn content written by hand is slow to produce and inconsistent in voice.
When I joined for my six-month end-of-studies internship there was one product, the content service, and only its basic post creation worked.
What I built
Three product services on one shared library, one database and one set of background workers. I did the analysis, the architecture and the implementation, under the CEO's supervision and with code review from the team.
- PortalIdentity, onboarding and workspaces for every tenant.
- TropiccLinkedIn content, with retrieval over each client's own material.
- OceanssGTM intelligence: hiring and market signals, anomaly detection, account scoring.
Wavess-Core, the shared libraryToken verification, tenant resolution, access gating, background jobs and LLM routing.
- Supabase PostgreSQLOne database, a schema per service.
- Redis and ARQ workersBackground jobs: scraping, enrichment, scans.
- LLM chainGroq, Cerebras, Gemini and NVIDIA NIM, with quota tracking.
The specification came first: 46 functional and 11 non-functional requirements and 15 user stories, linked in a two-way traceability matrixSource: report tables 2.5 to 2.9, A.3. 2026.. I modelled the content and GTM flows as use cases and BPMN processes, and scored the priority of every requirement for the CEO to review. Delivery then ran in 6 sprints from February to May 2026Source: report p.28. 2026., in three phases.
- Rebuild the content service (Tropicc). It was partly working when I joined, and about 90% of the service was refactored or rebuiltSource: report p.27. 2026.: the content feed, organization settings, the generation pipeline and the LinkedIn integration. It writes posts in each client's brand voice, grounds product posts in the client's own documents, predicts engagement before publishing and schedules across timezones. A person approves every draft before it is scheduled.
- Extract the shared core and build the Portal. Authentication, tenant handling and shared utilities had ended up duplicated or scattered. I moved them into one Python library that every service imports, and built the Portal as the one place where people sign up, organizations are created and product access is decided.
- Build the GTM intelligence engine (Oceanss). It collects hiring, media, feedback and financial signals, flags unusual activity against each company's own baseline, scores accounts by hiring intent and gives each client a dashboard built around the goal they chose.
Every request passes five access layers: token verification, tenant resolution, organization entitlement, user entitlement and a route guardSource: report Table 3.1, p.67. 2026. before it reaches business logic. Every model call goes through one router.
One router in Wavess-CoreEvery model call from Portal, Tropicc and Oceanss, routed by task type and estimated size.
- GeneralEvery other request.
- PrimaryGroq Scout
- FallbackGroq Fast, Gemini Flash-Lite
- Last resortNVIDIA NIM
- ClassificationShort classification tasks.
- PrimaryCerebras
- FallbackGroq Fast
- Last resortNVIDIA NIM
- Long contextRequests with a long input.
- PrimaryGemini Flash-Lite
- FallbackGroq Scout, Gemini Flash
- Last resortNVIDIA NIM
Quota counters in RedisOne per provider per day. A provider close to its daily cap is skipped before it starts refusing calls.
Only successful calls count against a quota. The design goal was serving more than 90% of requests within free tiersSource: report p.42 NFR-COST-01. 2026. A goal, not a measured result.. That is a target from the requirements, and the report does not measure it.
Decisions that mattered
I proposed these and the CEO reviewed them. My report records the alternatives and the reasons for each.
ADR-WV-01Federated services, not a monolith or microservices
- Context
- Content generation and signal collection have different workloads, data models and release rhythms. The team was small, and hosting on Fly.io came with tight memory and cost limits.
- Decision
- Three coarse-grained services (Portal, Tropicc, Oceanss), each with its own schema and its own deployment, on one shared library. A monolith was rejected because one deployment cannot scale content generation apart from signal collection, and a shared schema would tie the two data models together. Fine-grained microservices were rejected because service-to-service communication, distributed transactions and separate databases were more than a small team could run.
- Trade-offs
- Moderate operational overhead: three deployments, and a shared library that all three have to stay in step with.
ADR-WV-04A shared library, not an API gateway
- Context
- Token verification, tenant resolution and LLM routing are needed on every request in every service. As the first service matured, this code had ended up duplicated or scattered.
- Decision
- Wavess-Core is a Python library that each service imports, not a gateway or a sidecar. These concerns sit inside each service's request lifecycle, and a separate network hop for them was not justified. Each service installs the library from Git, pinned to a commit, so builds are reproducible. Platform mechanics stay in the library; business logic stays in the services.
- Trade-offs
- The library is not deployed on its own: a fix reaches a service only when that service moves its pin and redeploys.
ADR-WV-11One LLM router, many providers
- Context
- A bootstrapped startup cannot pay per token for every call, or depend on one vendor staying up. The cost requirement set a goal of serving more than 90% of requests within free tiers.
- Decision
- Every LLM call from every service goes through one router in the shared library, with routes across Groq, Cerebras, Gemini and NVIDIA NIM. Daily usage per provider is counted in Redis. A provider is skipped at 95% of its daily cap, before it starts returning rate-limit errors, with automatic fallback to the next one. A single paid vendor was rejected as a single point of failure with no free tier, and self-hosted models for their GPU cost.
- Trade-offs
- The free-tier goal is a design target; the report does not measure it. When every provider in a chain is exhausted, the request fails with a 503.
What broke, and what I changed
Onboarding trusted the model too early. The Portal fills a new organization's profile from its website with an LLM, and the flow had to be revised once I saw what extracted profiles looked like in real use. The version that shipped stores the extraction as a draft. The user corrects and confirms it, and only then does it become the organization's record. Running the extraction again later shows a difference per field, and the user picks each change.
User keywords polluted competitor scans. The hiring scan builds its search queries from keywords. In competitor scans, keywords typed by users tended to pollute the results, so those scans now build their queries from the model's analysis alone. Customer-profile scans still merge the user's keywords in.
One feature was cut to ship another. A competitor generation system for the content service was planned for the last phase. It was deferred so the GTM engine could reach production.
Results
- B2B clients in Germany, the UK and the Czech Republic
- 6Source: report p.10 to 11. 2026.
- platform parts: three services on one shared core library
- 4Source: report p.52 to 54, p.65. 2026.
- requirements traced to user stories
- 57Source: report tables 2.5 to 2.9, A.3. 2026.
- LLM routes across 4 providers
- 6Source: report p.67 to 68. 2026.
Both products were in production use while I worked on them.
What these numbers do not show: the tests I wrote run against mocked providers, I did not get to load testing during the internship, and I did not measure the accuracy of the model-based scoring.
The router, the quota tracking and the approval step before anything reaches a customer are the patterns I rebuilt on free tools in the Personal GTM Engine and Job Radar.
What I would do next
- Load tests and tests against live APIs, so behavior with many tenants at once is measured, not assumed.
- Let the two products talk: signals from the GTM engine should suggest topics in the content service. The platform was designed around that link, and it is not built yet.