Scenario · B2B configurator · Custom manufacturing

From quotes by email
to a real-time configurator.

A company that makes custom components and prepares every quote by hand, across price lists, spreadsheets and questions to the engineering office. A web configurator that calculates price and item code while the customer chooses.

Project typeB2B configurator
SectorCustom manufacturing
Typical timing10–14 weeks
●Scenario
web app · B2B configurator
B2B
PRODUCT CONFIGURATOR
User: engineering office
1
Type
2
Parameters
3
Materials
4
Quote
Product parameters
Nominal diameter
DN 50
DN 80
DN 100
DN 150
Max pressure (bar)
16 bar
25 bar
40 bar
Body material
Stainless steel
Cast iron
Bronze
Configuration summary
ITEM CODE
VB-50-25-INOX-F
Body base€ 2.100
Stainless material ×1.8€ 1.680
Pressure 25 bar€ 350
Customer discount (15%) − € 617
NET PRICE
€ 3.513
GENERATE PDF

Web app scenario — B2B product configurator

300 product variants.
Every quote made by hand.

The scenario: a maker of custom industrial valves and hydraulic components, supplying process plants, shipyards and the food industry across Europe. The products are highly configurable: diameter, nominal pressure, body material, connection type, internal lining, certifications. Combining the variables gives hundreds of possible configurations, each with a different price.

Without a configurator, every quote request follows the same loop: the salesperson collects the specs, emails them to the technical office, which checks feasibility and passes the data to the pricing office, which prepares the quote in Excel and sends it back to sales. Typical time: 3–5 working days.

In a market where foreign competitors reply within 24 hours, this process becomes a real disadvantage: some deals are lost purely on response time.

«We already know we can make the product. The problem is that the customer doesn't — because it takes four days to tell them. Meanwhile they've already called someone else.»

— What I often hear in manufacturing companies
Scenario B2B configurator with real-time pricing and quote generation (example)
Stack Node.js + Express · React · PostgreSQL · Redis
Integrations CRM (e.g. HubSpot) · Puppeteer (PDF) · email via Nodemailer
Users Internal sales team · Agents · Direct customers (self-service)
Typical duration 9–10 weeks
Tags
Node.js React pricing engine PostgreSQL Redis HubSpot API Puppeteer

Industrial B2B configurator requirements

From days to minutes.
With no configuration errors.

Guided step-by-step configurator with real-time validation

The system must guide the user through the product parameters, showing only technically valid combinations. If you select DN 150 with 40 bar pressure, the "bronze" material must disappear from the list — because that combination isn't certified. Compatibility rules are handled server-side, not client-side.

Real-time pricing with customer-specific price lists

The price must update at every parameter change, without clicking "calculate". Each B2B customer has a personalized price list (discounts, surcharges for certifications, locked prices on some product families). The pricing engine must apply the right rules for the logged-in user, without ever exposing the base price list to the frontend.

One-click branded PDF quote generation

The quote PDF must include: company header, customer data, full technical description of the configured product, net price with breakdown, supply terms, quote validity and the salesperson's digital signature. Generated in under 5 seconds and emailed to the customer automatically.

Automatic sync with HubSpot CRM

Every generated quote must automatically create a deal in HubSpot with all the configuration details, the quote value and the assigned salesperson. If the customer already exists in the CRM, the deal is linked to the existing contact. If they're new, the contact is created. No double manual entry.

Self-service access for direct customers with a private area

The largest customers must be able to access the configurator directly, without going through a salesperson. They see only their own price list and the history of previous quotes, and can start again from a saved configuration. The sales office is notified of every new quote generated by a customer on their own.

Technical challenges of a real-time pricing engine

A pricing engine
that never lies.

A wrong configurator is worse than none: if it produces a wrong price or accepts an impossible combination, the company has to renegotiate an offer already accepted. The main challenge isn't technical — it's absolute correctness.

01

Modelling hundreds of compatibility rules without hard-coding them

Compatibility rules between parameters (e.g. "DN 150 + 40 bar → stainless steel only") are usually scattered across years of technical documents, emails and knowledge in technicians' heads. The answer is a rule engine on PostgreSQL: each rule is a row with conditions and consequences, editable by the technical office without touching code. The engine evaluates rules by priority and returns the valid options for the next step.

02

Pricing engine: several price-list levels, nothing exposed to the browser

The system handles several kinds of price list (standard, agent, direct customer, framework agreement, promotion, locked price). Calculation happens only on the Node.js server: the front end receives the final price and the breakdown that user is allowed to see. The base price list never leaves the API. Redis caches price lists for 15 minutes and takes a lot of load off the database at peak times.

03

PDF generation with Puppeteer: an on-brand template

The quote PDF has to match the paper format used for years — a header precise to the millimetre, complex technical tables, the salesperson's signature as an image. Puppeteer turns an HTML/CSS template into a PDF with calibrated margins. It runs in a separate process so it doesn't block the server, with a 10-second timeout and automatic retry.

04

HubSpot sync: handling duplicates and partial updates

HubSpot has no atomic "create or update": if you search a contact by email and don't find it you have to create it — but meanwhile another process may already have done so. The answer is idempotency based on a unique quote ID: each sync first checks whether a deal with that ID already exists in a custom field, and if so updates it instead of duplicating.

05

Configuration session: recovery after refresh or disconnection

A complex configuration takes several minutes. If the salesperson refreshes the page or loses connection, they need to pick up where they left off. State is saved automatically to PostgreSQL every 30 seconds and restored on reopening, even from another device.

B2B configurator development process

9–10 weeks to turn
tacit knowledge into software.

Weeks 1–2
Extracting technical knowledge

Two weeks with the technical lead and the most experienced salesperson to map every compatibility rule. They're usually not documented: they live in people's heads and in some internal Excel file. The result is a structured document with every rule in the form "IF [parameter A] = X AND [parameter B] = Y → THEN [parameter C] cannot be Z", which becomes the engine's data model.

→ rules mapped · database structure · completeness check with the technical office
Weeks 3–5
Node.js backend: rule engine + pricing engine

The heart of the system: the Node.js rule engine reads rules from PostgreSQL, evaluates them by priority and returns each step's options. The pricing engine applies the logged-in user's price list — base price × material coefficient × pressure coefficient + options, then the contract discount. Redis caches price lists for 15 minutes. Load tests to check it handles hundreds of requests per second.

→ Express endpoints · rule engine · pricing engine · Redis cache · Jest tests
Weeks 6–7
React frontend: step-by-step configurator + autosave

A step-by-step React interface: each choice asks the backend for the next step's valid options. The price updates in real time with a 300 ms debounce on the call. State is saved to PostgreSQL every 30 seconds — close and reopen the browser and everything is as it was. Plus a "quote history" for customers with direct access.

→ React + 300ms debounce · 30s autosave · quote history · tablet responsive
Week 8
PDF generation with Puppeteer + HubSpot integration

The PDF template is built in HTML/CSS and faithfully reproduces the existing paper one. Puppeteer turns it into a PDF in a few seconds in a dedicated process. The file goes to S3 and is emailed to the customer and the salesperson. In parallel the same quote reaches the CRM via API: contact created or updated, a deal with every configuration field, assigned to the salesperson.

→ Puppeteer worker · S3 upload · Nodemailer · HubSpot API v3 · idempotent upsert
Weeks 9–10
UAT with the sales team and go-live

Two weeks of testing with some salespeople and external agents. It's normal for a couple of missing rules (very rare cases never mentioned in interviews) or a discount misapplied on a product family to come up: with the configurable rule engine they're fixed without a new release. Gradual go-live: internal sales first, then agents, then self-service customers after a few weeks of stability.

→ tests with sales and agents · rules completed · 3-phase launch · no downtime

Solution: Node.js rule and pricing engine

The rule engine:
hundreds of constraints, none in the code.

The most important architectural choice is separating business rules from code. Every time the technical office adds a variant or changes a compatibility, it edits a row in the database — no new release.

Solution 01 / 03

Configurable rule engine — editable without code

Each rule is a PostgreSQL row with four fields: param_key, param_value, excluded_key, excluded_values and a priority for conflict handling. The Node.js engine loads the rules relevant to the current configuration, evaluates them in order and returns an array of valid options for the next step. The technical office manages the rules from an admin panel built with React Table — no developer involvement needed to add or change constraints.

// pricing-engine.js — price calculation with Redis cache async function calculatePrice( config, userId ) { const cacheKey = `listino:${userId}`; let listino = await redis.get( cacheKey ); if ( !listino ) { listino = await db.query( 'SELECT * FROM listini WHERE cliente_id = $1 AND attivo = true', [userId] ); await redis.setEx( cacheKey, 900, JSON.stringify(listino) ); // 15 min TTL } else { listino = JSON.parse( listino ); } const basePrice = getBasePrice( config.tipologia, config.diametro ); const coefMat = listino.find( r => r.param === config.materiale )?.coef ?? 1; const coefPress = listino.find( r => r.param === config.pressione )?.coef ?? 1; const sconto = listino.sconto_cliente ?? 0; const lordo = basePrice * coefMat * coefPress; return { netto: lordo * (1 - sconto), lordo, sconto, breakdown: { basePrice, coefMat, coefPress } }; }
Solution 02 / 03

PDFs in seconds — identical to the paper template

Puppeteer renders an HTML/CSS template with the quote data filled in on the server. The template reproduces the company's paper format: header with vector logo, technical parameter table with units, price breakdown, supply terms, validity and the salesperson's signature. The Puppeteer process is separate from the main server, with a 10-second timeout and automatic retry. Goal: a quote ready in under 5 seconds.

Offerta_VB-50-25-INOX.pdf
QUOTE NO. 0847 (example)
Date: 15/11
VB ball valve · DN 50 · 25 bar · Stainless steel
Diameter
DN 50
Pressure
25 bar
Net price (15% discount)
€ 3.513,00
Sent to customer · HubSpot updated
Solution 03 / 03

An always up-to-date CRM — without touching HubSpot

Each quote triggers a background sequence: first the PDF goes to S3, then CRM sync starts. The contact is looked up by email — updated if it exists, created if not. Then the deal is created or updated with an offerta_id field as the idempotency key: if the job runs twice, it doesn't create a duplicate. Salespeople find every quote in the CRM already linked to the right contact, with the PDF attached, the value and the "Quote sent" stage. Goal: zero manual entry.

Why it matters: the sales manager stops doing the "evening round" to update the CRM by hand, and the data stays clean, with no duplicates.

Node.js React PostgreSQL tech stack

Node.js at the center.
Every tool in its place.

Node.js + Express
React
PostgreSQL
Redis
Puppeteer
CRM API (e.g. HubSpot)
AWS S3
Nodemailer + SES
Jest + Supertest
Vite
React Table
Zod (validation)

Why Node.js and not PHP/Laravel?

The pricing engine evaluates dozens of rules in sequence for each request: fast but very frequent work. Node.js handles well the concurrency typical of an API making many Redis and PostgreSQL queries in parallel. Using TypeScript on both front end and back end reduces context switching and interface bugs between the two layers.

Why Puppeteer and not a PDF library?

Libraries like PDFKit or jsPDF require building the layout programmatically — every line, every table, every margin defined in code. An HTML/CSS template is infinitely more maintainable: a designer can change the look of the quote by editing CSS, without touching JavaScript. The price is a slightly heavier process, solved with the isolated worker and pre-warming Puppeteer when the server starts.

Example goals — B2B configurator

From days to minutes.
Every quote tracked.

Example figures, not a client's results: realistic goals for a scenario like this, to be measured in the first months after launch against the same period before.

−90%
Quote cycle
goal: from days to minutes
100%
Quotes generated
all in the CRM, no pricing errors
−90%
Configuration errors
goal for fewer corrections
+20%
Close rate
goal for more accepted quotes
<5s
PDF generation
generation time goal
0
HubSpot duplicates
goal with idempotent sync

«A customer sends an order through direct access at 10:30 p.m. on a Friday. Before, they'd have waited until Monday.» That's the scenario to aim for: a company that answers even when the office is closed.

— The concrete goal of self-service access

Product configurator development learnings

What I bring
to every project like this.

→

Separating business rules from code pays off immediately. After launch new variants and compatibilities always arrive: with the rule engine the technical office handles them from the panel, with no development work.

→

Prices are always calculated on the server, and only there. Calculating in the browser would cut calls but expose the price-list coefficients to anyone opening developer tools — handing your pricing structure to competitors. With a 300 ms debounce the experience still feels real-time.

→

Autosave is always worth the cost. One accidental refresh is enough to lose ten minutes of configuration: for any web app with complex forms it's non-negotiable.

→

Idempotent sync jobs aren't a detail. In production a job sometimes runs twice (timeout + retry). Without a check on a unique ID, the CRM fills up with duplicates in the first days after launch, exactly when data quality matters most.

→

Direct access for B2B customers changes the relationship more than any feature. Some quotes arrive outside office hours, and a transparent, fast process builds a different kind of trust than a phone call with a salesperson.

Do you have a sales process that deserves to be automated?

Configurators, quotes, complex catalogs — if the logic already exists, it can be coded.

Have a similar project in mind?

Tell me about it: we start from goals and priorities, and the first call is free.