Est.

API Design for a Jewelry Configuration and CAD Generation Service

Jewelry APIs must compute physically valid objects in milliseconds, not just return SKUs.

Senior Writer · · 11 min read
Cover illustration for “API Design for a Jewelry Configuration and CAD Generation Service”
Jewelry Product Configurators · August 20, 2026 · 11 min read · 2,423 words

Building an API for jewelry configuration is nothing like building one for t-shirts. A t-shirt API hands back a SKU: size medium, color blue, done. A jewelry configuration API has to compute a physically valid, one-of-a-kind object every single time someone touches a slider. Get that wrong and you're not shipping a bug, you're shipping a ring that snaps in half at the prongs.

The market's making this urgent whether engineering teams like it or not. Online jewelry sales are on track to hit $85.7 billion by 2026, growing around 13% a year. Close to 45% of those online purchases already involve some kind of personalization, which means almost half the transactions can't be served by a static product lookup. And AI-driven personalization tools are expected to push past a third of fine jewelry sales by 2030. The demand for "build it your way" isn't a nice-to-have anymore. It's the main event.

Three things make this API category genuinely different from anything in a standard REST tutorial. First, the output is a file that becomes a physical object with real consequences. Second, none of the parameters are independent. Bump the stone size and you've just invalidated the prong height, the wall thickness, maybe the whole band. Third, the latency budget is split down the middle: the configurator needs to feel instant, while the CAD generation step can take its time, as long as it never falls over.

What follows is how you actually build for those three constraints, in order.

The parametric dependency graph as the API's core data model

Here's the thing people miss early on: a jewelry piece isn't a list of attributes sitting next to each other. It's a web of dependencies, and pulling on one thread moves five others.

Stone diameter sets the minimum bezel wall thickness. That sets the minimum band width. That sets the weight, and weight sets the price. Change the metal alloy and you've changed which casting tolerances are even achievable, which then limits how thin a prong can be. Switch the shank profile from knife-edge to comfort-fit and the cross-section geometry shifts, which means ring size no longer maps to inner diameter the same way it did a second ago.

So don't model this as a flat JSON object with a bunch of keys. Model it as a directed graph.

  • Nodes are individual parameters: stonediametermm, metalalloy, shankprofile, prongcount, ringsize.
  • Edges are the dependency rules: when node A changes, which nodes need to be re-checked, and what's the new range of valid values for each.

Some nodes are root nodes, meaning a user picks them directly. Others are derived, meaning the API computes them and the user just sees the result. This distinction matters a lot when you're deciding what's editable and what's enforced.

A few practical consequences fall out of this. The configuration session object has to carry the entire graph state, not just whatever the user clicked last. When a validation check runs, the response needs to return the full updated valid-range envelope for every parameter that got touched, not a plain pass or fail on the one thing that changed. And you want to version the graph schema separately from the API version itself, because a manufacturing partner might tighten a tolerance spec next quarter without you touching a single API endpoint.

Take a solitaire ring configurator as a concrete case. A customer drags the stone size from 5mm to 7mm. In one response, the system needs to: knock out the 3-prong option (it's not structurally valid at that diameter anymore, so now only 4 or 6 prong options remain), raise the minimum band width, and recalculate the price. All in one round trip, all before the customer notices anything happened.

This graph isn't just a UI nicety, either. It's the source of truth the CAD engine reads from. If what the configurator shows as "valid" doesn't match what the CAD engine can actually build, you end up with a file nobody can cast.

Separating the configuration session layer from the CAD generation layer

Diagram: Two-Layer API: Configuration vs. CAD Generation. Visualizes: Visualize the strict separation between the configuration session layer and the CAD generation layer, showing each layer's endpoints, characteristics, and the locked-snapshot…

These are two completely different jobs wearing the same trench coat, and treating them as one service is where a lot of teams get into trouble.

The configuration layer is light. It's stateful, it's synchronous, and it needs to respond in milliseconds, because every parameter tweak, price update, and preview render happens here. The CAD generation layer is the opposite: heavy, stateless per request, and fine with taking a few seconds or even minutes, as long as it's reliable when it finally answers.

Split them into two distinct APIs.

The configuration layer runs as a session API:

  • POST /sessions creates a session and returns the initial valid-parameter envelope.
  • PATCH /sessions/{id} applies one change and returns the full re-computed state, constraints and pricing included.
  • GET /sessions/{id}/preview returns a lightweight render for the configurator screen.

Give sessions a TTL. A session that never gets finished shouldn't be sitting around waiting to accidentally trigger a CAD job.

The CAD layer runs as a job API:

  • POST /cad-jobs takes a locked, validated snapshot and returns a job_id right away.
  • GET /cad-jobs/{id}/status polls for queued, processing, complete, or failed, with real error detail attached.
  • GET /cad-jobs/{id}/artifacts hands back signed download URLs once the job's done.
  • Support webhooks too, so manufacturing systems aren't stuck polling in a loop like it's 2009.

The handoff between these two layers is a locked snapshot, and it needs to be treated like a contract. Once a configuration gets submitted to the CAD queue, it's frozen. Any edit after that point starts a new session; it does not reach back and mutate a job that's already running. The snapshot itself needs to carry the full resolved parameter set, the graph schema version, and the manufacturing profile ID (tolerance set, casting method, target printer spec).

Why go through the trouble of separating these? Cost and scale don't behave the same way. The configurator scales horizontally and cheaply, like most web services do. CAD generation is GPU or CPU heavy, sometimes running on third-party compute, and it scales on a totally different curve. Bolt them together into one service and you've coupled two workloads that have nothing in common except that they both touch the same ring.

Manufacturing constraints as first-class API citizens, not post-processing checks

There's a cost asymmetry here that should shape the whole architecture: an error caught on screen costs nothing. An error caught after the metal's been cast costs real material and a restart. And we're talking about fractions of a millimeter causing actual structural failure, not cosmetic issues, a prong too short to hold its stone, a wall too thin to survive the polishing wheel.

So manufacturing rules can't live only inside the CAD engine, checked at the very end. They need to sit inside the API layer itself, right alongside everything else.

What do those rules actually look like?

  • Minimum wall thickness, and it's different for yellow gold, white gold, platinum, and sterling silver, because they all cast and finish differently.
  • Stone seat depth: deep enough to hold the stone, not so deep it weakens the base underneath.
  • Prong geometry: minimum height relative to the stone's girdle, minimum tip radius so it doesn't wear through in five years.
  • Ring size to inner diameter mapping, which isn't linear and shifts depending on shank profile.
  • Engraving depth versus band wall thickness, since deep engraving on a thin band is basically inviting a crack.

Bundle these into named, versioned manufacturing profiles. A profile packages the alloy tolerances, the casting or printer specs, and the finishing allowances together. Different fulfillment partners will run different profiles, so the API needs to support several active profiles at once and route each order to the right one.

When a constraint gets violated, don't throw a generic 400 and call it a day. Return a structured object naming the exact rule that failed, the value that triggered it, and the range that would've passed.

The anti-pattern worth calling out directly: running constraint checks only inside the CAD engine, after a multi-minute generation job finishes. By the time that failure comes back, the customer may have already paid. The whole point of real-time re-constraint on every PATCH /sessions call is that it makes upstream enforcement actually work; nobody ever gets shown an invalid option in the first place.

Output file formats and what production-ready actually means for each

STL is the floor here, not the finish line. It's a triangle mesh, and it's what most resin and wax printers need to produce a castable model for lost-wax casting. But exporting to STL is where mesh errors like holes, inverted normals, overlapping surfaces, and non-manifold edges tend to sneak in, and every one of those confuses a printer. Tools like Netfabb or Meshmixer catch and fix this kind of thing, and in an API, that check needs to run automatically the moment a job completes. Not as some manual step someone remembers to do on a Friday.

STL is also a one-way door. It can't be re-parameterized later by a designer working downstream. So for enterprise or wholesale clients who need to keep editing a piece in Rhino, MatrixGold, or JewelCAD, the API should also output native or interchange formats like 3DM or STEP, wherever the generation engine can produce them. Let format selection be a parameter right on the POST /cad-jobs call, since different fulfillment paths need different files from the exact same configuration.

The configurator's rendering layer is a separate concern entirely. It needs PBR material maps, meaning separate albedo, normal, roughness, and metallic layers, so a WebGL viewer can render metal and gemstones correctly from any angle. Baking lighting into a flat texture gives you one nice-looking angle and a broken one everywhere else. And this preview asset should never get confused with the actual manufacturing file in the API's response schema; they come out of completely different pipelines.

Watertight geometry should be a guarantee, not a hope. A production-ready STL coming out of this API ought to pass manifold checks without anyone touching it by hand, and that's the line that separates an API-generated file from a raw geometry dump. Work on frontier 3D generation engines, including approaches like Direct3D-S2 presented at NeurIPS 2025, shows clean topology on the first pass is achievable now. That should be the stated bar for the API, not a "we'll try."

Whatever comes back from a completed job should include the file format, file size, triangle count, the manifold check result, a wall-thickness scan summary, and the manufacturing profile version used. That's enough metadata for a downstream system to accept or reject the file without a human opening it up first.

Real-time configurator performance and the API patterns that make it possible

The bar here is simple to state and hard to hit: when someone drags a slider or swaps a stone, the 3D view has to update fast enough that it feels alive. Any real lag and you've broken the moment someone was about to buy something. WebGL renders right in the browser with no plugin needed, but the rendering quality is what tells the customer whether what's on screen actually matches what shows up in the mail.

For the finite stuff, metal type, stone type, standard sizes, don't compute geometry live on every change. Pre-generate and cache the parameterized 3D assets ahead of time. Then PATCH /sessions just swaps which cached asset it's pointing to and hands back a CDN URL, instead of running geometry math on every keystroke. Draw a clear line: which axes are pre-baked (metal, stone, setting style) and which get computed live (engraving text, exact size within a fine-grained range).

For anything more open-ended, use progressive rendering. Send back a low-poly preview instantly, kick off a higher-fidelity render in the background, and push the upgrade through WebSocket or server-sent events once it's ready. Include a render_quality field in the session response so the client always knows what it's looking at and whether something better is on the way.

Pricing has to move in lockstep with every change, no exceptions. Metal weight (which comes from the geometry), stone type, ring size, engraving, all of it needs to recalculate on every single PATCH response. A price that looks one way during configuration and another way at checkout is how you lose a sale and a customer's trust in the same click. Make the pricing engine a pure function of the locked parameter set. Not a lookup table that can quietly drift out of sync.

On caching: session state is specific to one user and should never sit in a CDN. Pre-computed 3D assets and material maps, on the other hand, are static for a given configuration and are perfect candidates for caching forever, use a content-addressed URL, a hash of the parameter combination, and you get aggressive caching with zero invalidation headaches.

Handling asynchronous CAD generation without breaking order workflows

Here's the timing mismatch nobody can avoid: CAD generation might take a few seconds or a few minutes, but order confirmation can't sit around waiting on it. A customer clicking "place order" wants confirmation now, and the CAD file might not exist for another ninety seconds.

So decouple them completely. Order confirmation means a valid, locked configuration was received. CAD artifact delivery is a separate signal entirely, one that fires later and tells manufacturing the file's ready to go.

Model the whole thing as a chain of events, not one long synchronous call: order created, configuration snapshot locked and archived, CAD job enqueued, job completes, artifact URL gets pushed to both the order record and the manufacturing queue. Every link in that chain should be observable on its own and retryable on its own, without dragging the earlier steps along for the ride.

Idempotency matters everywhere in this chain. POST /cad-jobs needs to be idempotent on the combination of session snapshot hash and manufacturing profile ID, so a network retry never spins up a duplicate job for the same order. And the artifact's storage key should derive from the configuration hash itself, so if the same exact configuration somehow gets ordered twice, the system reuses the cached file instead of paying to regenerate something it already made.

Failure needs its own explicit states, too. A CAD job that silently times out and vanishes is worse than one that fails loudly with a reason attached, because at least the loud failure gives someone downstream a chance to fix it before the customer notices anything went wrong.

Sources

  1. branvas.com

More in Jewelry Product Configurators