# AI design and quoting platform for a pool and landscaping distributor

> We built a prototype that turns a backyard photo into a priced pool render, and designed the platform on the distributor's Adobe Commerce site and Okta.

Sector: Distribution
Stack: Adobe Commerce, Okta, Temporal, Databricks, PostgreSQL, Next.js, Three.js, Kubernetes
Canonical: https://www.techfabric.com/case-studies/ai-design-and-quoting-platform-for-a-pool-and-landscaping-distributor

---

## Challenge

**Homeowners were choosing a backyard from tile chips and showroom vignettes, and the distributor wanted every design priced from products it sells.**

Our customer Trellis Trades provides specialized software built for local home service and trade businesses, including pool builders, landscapers, and residential contractors ([source](https://trellistrades.com)).

Many companies that are in the trade businesses compete aggressively to get the business from customers. The faster they act, the more likelihood of them getting the contract. Trellis wants to help them close the deals faster, going from a walk around the property to a priced, branded quote in the same visit ([source](https://trellistrades.com)), and that's only possible with AI: the render that gets them there is generated on the spot.

The client is a distribution company that supplies pool and landscaping contractors through a network of branches. A homeowner planning a pool judged the whole project from a single tile sample and small coping and finish swatches, and the contractor hand-built the estimate and the material list afterwards. The distributor wanted homeowners to see their own finished project in their own yard, with several options side by side, before anyone broke ground.

General-purpose AI image tools already make pleasing backyard pictures, and they stop at the picture. The tile in the image is rarely a product the branch stocks, the water color is a guess, and nothing in it can become a quote. The distributor wanted every render built from SKUs in its own catalog, a bill of materials that matches what the homeowner sees, and prices for the contractor's own account.

The platform also had to fit the systems contractors already use every day. They sign in to the distributor's B2B site on Adobe Commerce through Okta, and pricing, branch inventory, promotions, checkout and orders already live there. The new platform had to open as one more tile on that dashboard, read prices and stock from Adobe Commerce and hand orders back to it, and it had to start with pools and carry over to landscaping without a second build.

## Strategy

**We designed the render around a parametric model and the real catalog, on the distributor's own platforms, with three forward-deployed engineers.**

A prompt that turns a backyard photo into a pool gets the look right and the materials wrong. Our design starts from a parametric model of the pool, where its shape, depth profile, features and the material on every surface are data, and the picture, the bill of materials and the later takeoff all come from that one model, so the quote always matches the render. A generative model still makes the result photo-real, but it works inside a mask and takes the catalog's textures and the computed water color as fixed inputs, so it can't change the products the homeowner chose.

In the design, Adobe Commerce and Okta stay the systems of record. The platform would read customer pricing and branch stock from Adobe Commerce and sign contractors in through the Okta session they already have, so there's no second login and no second price list. Anything that has to finish or resume, from a render job to an order, would run as a durable workflow, and the platform would be built once for pools as a set of category packs, so landscaping arrives as configuration.

Three forward-deployed engineers would carry the MVP, meeting the distributor's experts, its commerce team and the pilot branches every week. They'd build on Fabric, our AI delivery system, the same pipeline we use on our own products, where delivery that once took a team of ten now takes three. For each scope line the engineer and the distributor's expert write the acceptance criteria together, governed coding agents draft the implementation, tests and documentation, the change passes automated gates for unit and contract tests, security scanning, accessibility and AI evaluations, and a senior engineer reads every change before it merges. That's what lets a team of three plan the MVP as a twelve-week build.

## Solution

**A prototype that runs from a backyard photo to a priced quote, and a production design meant to keep it accurate, secure and durable.**

We built a prototype that takes a contractor's photo of a real backyard and renders a pool design into that yard. In the prototype, changing the tile, coping, decking or water features updated the render on the spot, the water's color shifted with the tile, the finish and the depth before anything was installed, and the same design produced a bill of materials and a priced quote. We designed the MVP to take that prototype into production, and most of that design work sits in the five parts below.

### From a backyard photo to a render

The MVP design starts with capture and measurement. Photo quality control would check exposure, blur and framing and ask the contractor for a retake while they're still in the yard, segmentation would separate the yard, the house, the hardscape and any existing pool, and a monocular depth model would produce metric depth anchored to the measurements the contractor confirms. Scene assembly would then place the parametric design into a 3D scene that matches the photo's camera, bind physically based catalog textures at their true scale and compute the water color for the chosen sky and lights.

A masked generative composite would make the result photo-real. In the design, a diffusion model conditioned on the depth map, the segmentation masks and the edges of the design would repaint only the area inside the design mask, so the house, the sky and the rest of the yard would stay pixel-identical to the homeowner's photo, and the catalog textures and the water color would go in as locked layers. A fidelity check would then measure the CIEDE2000 color difference between each rendered material and its reference swatch and run an artifact detector. A render that fails the check would be regenerated once, and if it fails again the pipeline would return the physically based composite, which stays faithful to the catalog even when it looks less photographic.

```mermaid
%% caption: The catalog's textures and the computed water color enter the generative pass as fixed inputs, so the photo-real render can't swap the products the homeowner chose.
flowchart TD
  A["Backyard photo"] --> B["Capture and measurement"]
  B --> C["Parametric design"]
  C --> D["Catalog textures and water color"]
  D --> E["Masked generative composite"]
  E --> F{"Fidelity check"}
  F -->|"pass"| G["Render for the homeowner"]
  F -->|"fail"| H["Physically based composite"]
  H --> G
  class C accent
```

In the MVP design, capture, depth and mask stages would be cached per photo, so changing the coping or the finish would rerun only the stages after scene assembly, which is what would make iterating with the homeowner in the room practical. We'd choose the models in the first four weeks against the distributor's own photos, comparing open-weight models on its GPUs with managed image APIs on quality, cost per render and data terms, and each model would sit behind an interface so it can be replaced as better ones appear.

### Modelling and calibrating water color

Water color is where a homeowner decides whether to trust the tool, so the MVP design computes it with a physical model. At the surface, a Fresnel term decides how much of the sky and sun is reflected at each viewing angle. Below it, light travelling down to the floor and back is absorbed and scattered according to the Beer-Lambert law with wavelength-dependent coefficients, so reds fade first and the color deepens from the sun shelf to the deep end. The finish and additive reflectance at the floor sets the color the water starts from, the depth from the design gives every pixel its water column, and after dark the selected pool lights replace the sun.

```mermaid
%% caption: Each finish's water color comes from a physical model whose parameters are fitted to photos of real chips and finished pools.
flowchart TD
  A["Finish and additive reflectance"] --> D["Water color model"]
  B["Depth from the design"] --> D
  C["Sun or pool lights"] --> D
  D --> E["Rendered water"]
  F["Photos of real chips and pools"] --> G["Fitted parameters per finish SKU"]
  G --> D
  E --> H["CIEDE2000 comparison"]
  F --> H
  class D accent
```

The design calibrates the model with the distributor's tile and finish experts. Physical chips and completed pools with known materials would be photographed under controlled D65 light with a color checker, the absorption, scattering and reflectance parameters fitted for each finish, and the rendered water compared with the reference photos of real chips and pools patch by patch using the CIEDE2000 color difference. The fitted parameters per finish SKU would be versioned in the catalog, so a design a homeowner has already approved keeps the colors it was approved with after any later recalibration.

### Enterprise security on Okta and Adobe Commerce

In the MVP design, a contractor starts from the Adobe Commerce account dashboard, where the platform appears as a tile. The tile would open the platform with the account context, and its backend for frontend would start an OpenID Connect authorization code flow with PKCE against Okta. Because the contractor already has an Okta session, the flow would complete silently and they'd sign in once. The backend would exchange the code for tokens on the server and issue its own httpOnly session cookie, so tokens stay on the server, staff would sign in through the same Okta tenant with MFA, and Okta groups would map to roles.

Every record would belong to a node in a tenant tree of the distributor, its regions, its branches, the dealers they serve and their users. In the design, each row in PostgreSQL would carry the path of the dealer that owns it, the backend would set the caller's path from the verified token at the start of every transaction, and row-level security would allow a row only when the caller's path is an ancestor of the row's path. A dealer would see its own projects, a branch its dealers' projects and the distributor all of them, and a mistake in application code couldn't widen that view. Roles would decide what a user can do inside it, so a homeowner could change a design without ever seeing the contractor's cost view.

```mermaid
%% caption: Each layer holds on its own, so a bug in application code still can't widen a dealer's view past its own branch of the tenant tree.
flowchart TD
  A["TLS and web application firewall"] --> B["Okta sign-on with PKCE"]
  B --> C["API checks tenant scope and role"]
  C --> D["PostgreSQL row-level security"]
  D --> E["Consent check before any render"]
  E --> F["Append-only audit log"]
  class D accent
```

In the design, the edge would terminate TLS behind a web application firewall, the API would check the tenant scope, role and input shape of every call and accept only persisted GraphQL queries, and the AI layer would run a consent check before any photo is rendered or used for training, with consent captured separately for each. Every change to a project would go to an append-only audit log, every AI-generated image would carry an illustrative-concept disclosure, and TechFabric Harness would record what each AI agent did and which data it touched. Pricing calls would go through a client generated from the Adobe Commerce OpenAPI specification, with contract tests on every change and nightly against the Adobe Commerce sandbox, so a change on the commerce side fails our pipeline before it can reach a branch. The MVP design targets OWASP ASVS Level 2 and deploys into the client's own cloud with customer-managed keys.

### Durable workflows for renders and orders

In the MVP design, every render is a Temporal workflow made of idempotent activities, so a failed GPU node or a timed-out model call would be retried from the step that failed without repeating the work before it. Temporal Cloud would run the workflow service with a namespace per environment and mutual TLS, we'd run only the workers, and the GPU node pool would scale to zero when no renders are queued. If Adobe Commerce is unavailable, a circuit breaker would trip and the bill of materials would show quantities with a price-pending state, so a design session carries on through a pricing outage.

We designed the order flow, which follows the MVP, the same way. When the homeowner accepts, a Temporal workflow with an idempotency key for the quote version would turn the accepted quote into an Adobe Commerce order on the contractor's company account. It would build the cart from the distributor's side of the quote, or check out the negotiable quote where there is one, with the contractor's existing payment terms, a freight method in checkout and the dealer's purchase order approval rules wherever its company account uses them, and the order would reach the ERP through the existing ERP integration the B2B site already runs, with the order numbers written back to the project.

```mermaid
%% caption: Every order step carries the quote version's idempotency key, so a retried call can't place a second order.
flowchart TD
  A["Homeowner accepts"] --> B["Temporal workflow with an idempotency key"]
  B --> C["Adobe Commerce order"]
  C --> D["Existing ERP integration"]
  D --> E["Order numbers back on the project"]
  B -->|"step fails"| F["Retry from the failed step"]
  F --> B
  G["Reconciliation every night"] --> C
  class B accent
```

When a step fails, the workflow would retry from the failed step, and the idempotency key means a retried call can't place a second order. A rejected order would release the cart and tell the contractor why, and a reconciliation job would compare the platform's projects with Adobe Commerce orders every night.

### One platform, one pack per vertical

Category packs are how the MVP design builds the platform once for pools and carries it to landscaping. A pack is a versioned manifest, validated against a schema and enabled per tenant node, that holds its category's catalog taxonomy, design element types, quantity rules, render prompt templates, recommendation constraints and theme. The shared engine would load the pack at runtime and hold no pool-specific logic, so adding landscaping means writing its pack, loading its catalog and calibrating its prompts. The plan would build the pool pack in weeks 3 and 4, before most of the pool-specific work, so every capability after that would run through it.

```mermaid
%% caption: The shared engine holds no pool-specific logic, so landscaping arrives as a second pack on the same render, catalog and quantity services.
flowchart TD
  P["Pool pack"] --> A["Shared engine"]
  L["Landscaping pack"] --> A
  S["Okta, Adobe Commerce and Databricks"] --> A
  A --> R["Render pipeline"]
  A --> Q["Quantity rules and bill of materials"]
  A --> C["Catalog and recommendations"]
  class A accent
```

The quantity rules in the pool pack would be small, tested functions that read the design geometry and convert each quantity into the units a branch sells, using the SKU's coverage and waste factor.

| Quantity | Derived from the design | Sale unit |
|---|---|---|
| Coping | Perimeter of the shell, less openings for spillovers and steps | Linear feet, then pieces |
| Waterline tile | Perimeter times the band height | Square feet, then boxes |
| Interior finish | Floor and wall area from the depth profile | Square feet, then bags |
| Deck | Deck outline less the pool and planting openings | Square feet, then pallets or pieces |

In the design, landscaping would reuse the same pieces. The render pipeline would treat the backyard as one scene, so pavers, stone, walls and plantings would come in as a second pack's design elements and prompts, through the same supplier ingestion and texture pipeline, and hardscape takeoff would run on the same quantity engine. The tenant tree, Okta sign-on, the Adobe Commerce integration, the AI governance and the data platform on Databricks would be shared by every category from the start.

## Results

**The prototype ran the whole path from a photo to a priced quote, and the MVP pilot is planned to measure the outcomes.**

The prototype took a contractor's photo of a real backyard to a render of the pool design in that yard, showed how the water's color changes with the tile, finish and depth, and generated a bill of materials and a priced quote from the design. General-purpose AI tools stop at the picture, as the distributor's own review deck puts it, and the prototype carried the design on to the quote.

The MVP is planned as a twelve-week build with three forward-deployed engineers, and it would be measured against the success measures the distributor set for the program, including quote creation time, close rates, average project size, upgrade attachment and material pull-through. In the plan, each measure is defined once in a KPI dictionary and built as a governed metric on Databricks, and the pilot dashboards read from there.

