← Back to client work
Indivd AB · HubSpot · 2026

The biggest deals were the slowest to quote. Now they are one document.

Indivd sells store analytics to retail chains where no two stores buy the same setup. Quoting a forty-store network meant several documents assembled by hand and kept in sync by hand. Today the seller configures once and the quote prices every store on its own terms, inside HubSpot, on a licence with no CPQ in it.

Three quotes, one negotiationAssembled by hand, kept in sync by hand.
Quote 17 stores · new cameras
Quote 233 stores · existing cameras
Quote 31 store · hardware only
One quote · 41 storesEach store keeps its own tier, hardware setup and rollout phase.
5 → 500

stores in one quote. The size of the network stops deciding how fast you can sell to it.

1

document per deal instead of a folder. One number, one signature, nothing kept in sync by hand.

0

licence upgrades and no second tool. It runs on the Sales Professional seat already paid for.

Live  with real customers since 3 August 2026.

01What changed

Five things a sales leader gets that did not exist before.

Same catalogue, same sellers, same licence. What changed is how many documents a deal needs, and how much of it anyone has to type.

Scale

One document per deal

A network of two hundred stores is one quote, not a folder of them.

Effort

Describe it once

The seller configures a product once and picks the stores. The system writes every line after that.

Risk

Nothing can disagree

Price, scope and rollout plan are generated from the same rows, so the document cannot contradict itself.

Control

Terms the CEO owns

Version, date and link to the general terms come from one record. No seller can edit them on a quote.

Cost

No licence upgrade

No Revenue Hub, no second tool, and every configuration stays in the CRM for reporting and renewals.

02See it work

One configuration in. Forty line items out.

The real card, rebuilt in the page and running on a loop. One product is configured, the stores are picked, and the deal fills itself. Saving a second time reconciles instead of duplicating.

A seller opens the deal, picks a product, sets the tier, the phase and the hardware setup, and selects the stores it applies to. Saving writes one line item per store. Saving again does not duplicate anything: it creates what is missing, archives what was removed, and leaves the rest untouched.

Documents to assemble, per deal
1 / 3
−2and nothing left to keep in sync by hand
Quote builder · inside the deal Running
01 Deal · Example Retail AB · 41 store records linked
Line items on the deal 0 items
01Drottninggatannew cameras · phase 1Journeys150 €
02Mall of Scandinavianew cameras · phase 1Journeys150 €
03Nordstanexisting cameras · phase 2Journeys150 €
04Emporiaexisting cameras · phase 2Categories800 €
05Frölunda Torghardware onlyStarter50 €
·36 more storeseach on its own tier, setup and phaseMixed·
+Setup BT · 7 storesexisting cameras · no installationOne time0 €
One line item per storeSaving again reconcilesNothing saved yet
The rule that makes saving safe to repeatcard_id, and the diff behind it

Every line item carries the identifier of the configuration that produced it. Saving again is a reconciliation, not an insert: the backend creates what is missing, archives what was removed and leaves the rest untouched. Add three stores in week six and the quote absorbs them.

Why it matters commercially: a seller can renegotiate a live deal without anyone rebuilding the quote by hand, and without the silent duplicates that make a total wrong.

What the card refuses to savefour validations, mirrored in the engine

At least one store. A phase, whenever the deal contains a subscription. Quantity locked at one for anything counted per store, with a single approved exception. Discounts inside 0 to 100, and an explicit volume discount on every recurring line, where zero is a valid answer.

/* Non hardware products are locked at quantity 1 because they are counted per
   location: quantity 2 would charge the same store twice. Enterprise is the one
   approved exception, matched by id and by name so it survives a rename and a
   recreation of the product. */
const QUANTITY_FREE_PRODUCT_IDS   = ['47257531981'];
const QUANTITY_FREE_PRODUCT_NAMES = ['enterprise'];

That lock covered 193 of the 198 products in the library, and it lived in two places: the card refuses to save, and the engine raises the same warning. Fixing only the front end would have produced a quote that saved cleanly and then complained about itself.

03At your size

Two hundred stores is not forty stores five times over.

Two inputs, and the rest is arithmetic rather than a promise. It sweeps by itself; drag the slider if you want your own number.

41stores in the network
Different setups they buy

A setup is a distinct way of buying: new cameras and installation, existing cameras, hardware only, a deeper service tier. A real catalogue carries dozens of them.

Before · by handAfter · one configuration
3 documents to assemble and keep in sync
→
1 document
3 totals the buyer has to add up
→
1 total
0 stores priced individually in the document
→
41 stores, each on its own terms
0 store rows created without anyone typing them
→
41 rows, written by the system
04What comes out

The document builds itself, and drops what does not apply.

Change the shape of the deal and the quote rebuilds. Nothing is written twice, and no heading ever renders without its value.

Shape of the deal

Pick one. The list of sections on the right rebuilds.

Quote assembly 16 sections
  • Header, quote number and validitygenerated
  • Prepared for, and fromgenerated
  • Executive summarygenerated
  • What Indivd enablesgenerated
  • Scopegenerated
  • What you getgenerated
  • Pricing summary, all storesgenerated
  • Pricing details, per storegenerated
  • Deployment plangenerated
  • Billing detailsgenerated
  • Questions your team may askgenerated
  • What our customers saygenerated
  • Where Indivd runs todaygenerated
  • Next stepsgenerated
  • Acceptance and general termsgenerated
  • Appendix, optional hardwaregenerated
Pricing, per store

Recurring and one-time kept apart

Discounts shown as a line rather than buried in a total, inside each phase.

Rollout plan

Generated from the same rows

Country, bundle, phase, hardware setup and store count, produced from the rows that make the prices.

Acceptance

Bound at a named version

Signature binds this quote, the supplementary terms and the general terms at a named version and date.

How the template gets its contentand the HubSpot limit that forced it

A quote template can only read what is associated with the quote, and custom properties on the Quote object itself arrive empty. So the deal became the rendering surface: the engine writes finished blocks of HTML into 14 deal properties, and the template injects them. The template holds layout and typography, and no business logic at all.

{% if DEAL.render_pricing_html %}
  <div class="ey">PRICING DETAILS
    {% if DEAL.flag_hardware_only != "true" %} · PER LOCATION {% endif %}
  </div>
  {{ DEAL.render_pricing_html|safe }}
{% endif %}
Where the legal terms livea settings table with no database

HubDB was not on the licence, so the general terms version, date and link sit on a single dedicated Company record used as a settings table. It is not a customer. The CEO edits it in HubSpot, every quote in every deal picks it up, and no seller can change the terms on their own document.

05Why it was built

Every product HubSpot sells for this needed a licence that portal does not have.

Six closed doors, and the six things built instead. They resolve one at a time. Click any door to drive it yourself.

ClosedRouted around

Coded quote modules

Went generally available in June 2026, and require Revenue Hub Professional.

A quote template inside a child theme, fed entirely by deal properties.

ClosedRouted around

Properties on the quote

Custom properties on the Quote object never reach the rendered document.

All variable content moved to the deal, which is associated and does reach it.

ClosedRouted around

HubDB

Not contracted, and it was the natural home for the legal terms.

One dedicated Company record acting as the global settings table.

ClosedRouted around

Serverless functions

Not available on this platform version or this licence tier.

A small Python service on a dedicated server, doing arithmetic and nothing else.

ClosedRouted around

Buyer-editable fields

The new CPQ supports them but not coded templates. The two are mutually exclusive.

Editing happens in the CRM, where it is recorded, and never in the document.

ClosedRouted around

Line items on a store

Line items belong to deals in HubSpot’s model and cannot hang off a location record.

One line item per product per store, each tagged with the store it belongs to.

0 of 6 blockers routed around, with the data still inside the CRM. Zero licence upgrades bought, zero extra tools introduced.
How the four layers fit togetherone diagram, in words

Locations are CRM records, archived and never deleted, so historical reporting keeps its ground. Configurations are line items. Terms are a record the CEO owns. The only thing living outside HubSpot is the arithmetic, which is the one part nobody needs to search, filter or report on.

06What it leaves behind

The quote is not a PDF. It is a row per store.

Everything the document prints stays in the CRM as structured data. That is the part that keeps paying after the signature.

One line item carries
StoreService tierPhaseTerm in monthsBilling startPriceCountryDiscount

How much of the network is actually live?

Live in phase 133
Waiting in phase 28

Coverage is a filter on the deal, not a question for the account manager.

What does this network bill every month?

Starter · 2100 €
Journeys · 334 950 €
Categories · 64 800 €
9 850 €recurring, every month

Forecasting stops depending on someone opening the PDF.

When does each store renew?

Phase 16 months
Phase 230 months

Every store carries its own term and billing start, so the renewal date is a field rather than a memory. Expansion is the same row with a later phase.

07What we take from it

The easy answer was to build the quote tool outside HubSpot.

That produces a beautiful PDF and a dead deal record: nothing to report on, nothing to renew from, nothing the next person can pick up. Everything here stayed where it mattered, and only the arithmetic moved out.

Your portal

Build your own configurable quote in HubSpot.

  • Priced per store, per site, per branch or per seat, on the objects you already have
  • No Revenue Hub and no second tool: this runs on Sales Professional
  • Live with real customers, not a prototype

Thirty minutes, your deal on the screen, no slides.

Book 30 minutes with Nils (opens in a new tab)