Back to insights
build-logengineeringnextjsbrevo

Build log: shipping a lead-capture flow without a marketing platform

Stéphane WillemsStéphane Willems5 min read

I'm going to do these "build log" posts now and then — short, honest write-ups of something I actually built recently and the decisions behind it. Not tutorials, not thought-leadership. Just the real reasoning, including the bits I'd do differently. If you're a Belgian business wondering what "senior engineering" actually looks like in practice, this is the closest thing to watching over my shoulder.

This one is about the lead-capture flow on this very site: the AI Readiness check, the free resources, the ROI calculator, and the newsletter signups. All of them feed one place. Here's how, and why I made the calls I did.


The goal, stated plainly

I wanted three different "soft" conversion points — a quiz, gated downloads, a calculator — to all drop an email into a mailing list, and I wanted to know which surface each subscriber came from. Without bolting on a marketing platform, without leaking API keys to the browser, and without making the site slower.

That's it. Small scope on purpose.

Decision 1: one tool, not three

The tempting version is to reach for a dedicated tool per job — a quiz platform, a form widget, an email service. Three subscriptions, three integrations, three things to break.

The site already used Brevo for the contact form (transactional email). Brevo also does contact lists and automations. So the honest move was: use what's already there. One account, one API key, one integration to reason about.

This is the kind of decision that doesn't show up in a demo but shows up in the bill and the maintenance load eighteen months later. Fewer moving parts is a feature.

Decision 2: the credentials never touch the browser

Every capture form on the site posts to a small server route (/api/newsletter), and that talks to Brevo. The API key lives server-side only — it's never in the client bundle.

This sounds obvious, but the common shortcut is to call the email provider directly from the browser with a "public" key. That works until it doesn't: public keys get scraped and abused. A thin server route is a few extra lines and removes the whole class of problem.

The route validates the input with a schema, then calls Brevo. If Brevo isn't configured, it returns a clean error and the form shows a friendly message — no stack traces, no half-broken state.

Decision 3: graceful degradation over clever fallbacks

Here's a small one I'm happy with. The newsletter route needs two things: an API key and a target list ID. If either is missing, the route returns a "not configured" response and the UI shows its normal error state.

No mock data pretending to work. No silent success that loses the email. If it's not wired up, it says so. When the real list ID gets dropped into the environment, it just starts working — no code change.

That honesty matters more than it sounds. The worst failure mode for a signup form is one that looks like it worked and quietly drops the address. I'd rather it visibly fail than silently lie.

Decision 4: tag the source, so the data is worth having

Every capture passes a sourcequiz, roi-calculator, lead-magnet:ai-policy, footer, and so on — stored as an attribute on the contact in Brevo.

It's one extra field. But it turns "we got some signups" into "the quiz converts, the footer doesn't, write more like the quiz." Instrumentation you add at build time costs almost nothing; instrumentation you try to add later means you've already lost the data you wish you had.

Decision 5: keep the maths honest and separate

The ROI calculator could have been a black box that spits out an impressive number. Instead, the calculation lives in a small, pure function — no UI, no framework — and it's deliberately conservative: it accounts for working weeks, caps how much of a task AI can realistically take, and nets off a rough build-and-run cost. It even says, on the page, "illustrative, not a quote."

Two reasons. First, a number you can defend is worth more than a number that's merely big. Second, pure logic is testable and easy to change — when I want to tune the assumptions, I touch one file, not a tangle of component code. Separating the "what it calculates" from the "how it looks" is one of those habits that pays off every single time.

What I'd revisit

A couple of honest notes. The capture flow currently adds the contact directly; for a stricter GDPR posture I'd likely move the newsletter to a double-opt-in (confirmation email) before launch — Brevo supports it, it's a small change, and for an EU audience it's the cleaner default. And the quiz state lives in the browser only; if I wanted to email people their full result breakdown, I'd pass the answers through to the transactional email. Both are deliberate "later, if it earns its place" calls rather than things I forgot.


Why I'm showing you this

Because the gap WDC fills is exactly this: a lot of people will advise you on AI and digital projects, but far fewer will sit down and build the thing properly — with the credentials handled right, the failure modes thought through, and the data instrumented so you actually learn something.

None of the decisions above are flashy. That's the point. Good engineering for a real business is mostly a series of small, boring, correct calls that add up to something you can trust and change later without fear.


I write these build logs and a short, practical newsletter for Belgian businesses — real engineering, the EU AI Act, and AI integration, in plain language. Subscribe below if you'd like the next one.

And if you've got a project where you'd rather have it built right the first time than advised on endlessly, that's what WDC does — start with a conversation.

Ready to start?

Talk to us about your project.

Most engagements start with a 30-minute conversation.

Book a call

Subscribe to our newsletter

Sign up for occasional, practical writing on AI integration, EU AI Act, and senior engineering for Belgian businesses.