How an integration fits together
A shopper builds a ring in the builder on your product page. Every change is priced on our server with your price book. A save or enquiry becomes a lead, and an add to cart becomes a cart line on your store. The order then comes back as a webhook. Your backend can do every step itself over the API, with the same prices the builder shows.
3D Jewelry Designer3 min read
The parts
- The builder runs in an iframe on your product page, loaded by
ring-builder.jsor a plain<iframe>. It draws the ring, prices it and talks to your page. - Our server prices every build from your price book, saves leads, writes cart lines and records orders. A price never comes from the shopper's browser.
- Your store (Shopify, WooCommerce or your own site) holds the cart and the order.
- Your systems (CRM, ERP, workshop) hear about leads, carts and orders by webhook, and can read and change everything over the REST or GraphQL API.
What happens when a shopper builds a ring
Your product page
| ring-builder.js loads
v
The builder (in an iframe)
| each change -> priced on our server
| save, enquiry -> lead.created
| design.saved
| add to cart -> cart.created
v
A cart line on your store
(a Shopify variant, a draft order,
our checkout, or your own code)
|
v
The order
Shopify order -> order.created
order.attributed
our checkout -> order.paid
- Each change the shopper makes is priced on our server with the
builder's price book, the diamond they chose and your engraving price. The
page hears it as
onSelection. - A save or an enquiry is saved as a lead in Dashboard → Leads, with the price, the diamond, the ring size and a link that reopens the exact ring.
- Add to cart goes to the cart route, which prices the build again and decides how to write the line: an existing Shopify variant, a new one, a draft order, our own checkout, or, on other stores, an event your page handles with the signed price.
- The order comes back: a Shopify order through the app's order webhook, traced to the builder by the line properties the cart wrote, or a payment in our checkout.
Prices you can rely on
Every price you receive, in an event, a webhook or an API answer, was worked
out on our server from your price book. When your own server sets a cart line's
price, check the signed quoteToken the builder hands over first (see
the SDK reference), or price the build yourself with
the quote endpoint below. The two give the same answer.
Headless: your own storefront
Build the picker yourself and let our server price it. Pass your builder's id so the quote uses its price book, stone and engraving, exactly as its cart will:
const API = "https://3djewelrydesigner.com/api/public";
const headers = { Authorization: `Bearer ${process.env.RMJ_API_KEY}`, "Content-Type": "application/json" };
const embedId = "YOUR_BUILDER_ID"; // Dashboard → Embeds, or GET /ring-builder/embeds
// 1. What can be built (scope ringbuilder:read)
const catalog = await (await fetch(`${API}/ring-builder/catalog`, { headers })).json();
// 2. The price this builder charges for one ring (scope ringbuilder:read)
const selection = {
bandSlug: "classic-round",
headSelection: { shape: "Round", carat: 1 },
metalPresetId: "yellow_gold_14k",
engravingText: "Always",
};
const { data: quote } = await (
await fetch(`${API}/ring-builder/quote`, {
method: "POST",
headers,
body: JSON.stringify({ embedId, selection, stone: { kind: "feed", diamondId: "DIAMOND_ID" } }),
})
).json();
// quote.sku, quote.priceCents, quote.currency; quote.stoneUnavailable if the diamond sold
// 3. Put it in the cart (scope commerce:write). A retry with the same key adds nothing twice.
const cart = await (
await fetch(`${API}/ring-builder/cart`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": "cart-8841" },
body: JSON.stringify({ embedId, selection, stone: { kind: "feed", diamondId: "DIAMOND_ID" } }),
})
).json();
// cart.instruction: how to write the line on your store; cart.priceCents is what it charges
Each add sends cart.created to your webhooks, with channel: "api". The
webhooks guide has a receiver that checks the signature.
Keeping products and 3D models in step
- Shopify: connect the store in Dashboard → Shopify. The catalogue sync matches your variants to the builder's parts by SKU, so a matched ring takes your product's title and price, and Bulk update pushes each variant's measured metal weight and stones to the product. See Shopify.
- WooCommerce: the plugin adds the builder as a shortcode or block, and a "Custom ring" product carries each configured ring with its signed price. See WooCommerce.
- Your own site: create and update builders, price books and stone sets over
/api/public/ring-builder/*, keep your 3D designs as projects (/api/public/projects), and handle the cart in your page'sonAddToCart.
How 3D models are delivered
Ring parts are stored as Draco-compressed GLB files and served from our file storage with a one-year cache: a changed part gets a new file name, so a cached copy is never stale. The Draco decoder is served from our own address and shared by every model on the page. A builder far below the top of the page waits to load until the shopper scrolls near it, and one at the top starts at once.
Related
Related
- The ring builder's JavaScript SDK
Mount a ring builder on any page with one script, read every selection and add to cart, and size, theme and tear it down from your own code.
- Webhooks: events, payloads and signatures
Send leads, saved designs, carts and orders to your CRM or backend as signed webhooks, and check each delivery with a few lines of code.
- API versions and changes
How the REST API, GraphQL, webhooks and the ring builder script change over time, and what you can rely on not to change under you.