3D Jewelry Designer

The jewelry studio, over a REST and GraphQL API

Everything in the dashboard is on the API. Create projects, read studio configs, manage embeds and check credits from your store backend, PIM or order pipeline.

REST API, OpenAPI 3.1 spec and a typed SDK for Node and the browser.

create-ring.ts
import { createClient } from "@3jd/sdk";

const rmj = createClient({ apiKey: process.env.RMJ_API_KEY });

// 1. Fetch the catalog
const catalog = await rmj.ringBuilder.catalog();

// 2. Server-side quote (SKU, price, measured weight and stones)
const selection = { bandSlug: "classic-round", metalPresetId: "yellow_gold_18k" };
const quote = await rmj.ringBuilder.quote(selection);
console.log(quote.specs?.metalGrams); // grams, or null if not measured

// 3. Save the quote for checkout
const saved = await rmj.ringBuilder.saveQuote(
  selection,
  { name: "Jane Doe", email: "jane@example.com" },
  "cart",
  { idempotencyKey: "cart-1234" }
);

Security and reliability

  • Scoped, hashed keys. Keys are stored as SHA-256 hashes, shown once, limited to the scopes you choose (models, embeds, usage) and revocable at any time.

  • Rate limits and idempotency. 120 requests a minute per key, with Retry-After headers. Every write accepts an Idempotency-Key, so a retry never creates a duplicate.

  • Tenant isolation. Every request is bound to your organization, and row-level security in the database backs every API check.

Embeddable ring builder

Publish a live 3D ring builder on any store. Customers choose band, shape, setting, carat and metal, and you receive the selection, the price and an add-to-cart event. Configure it in Dashboard → Embeds → Ring builder (default ring, allowed metals, AR, price, button, branding and domain lock).

embed.html
<!-- 1. iframe: drop it anywhere -->
<iframe src="https://app.example.com/embed/SLUG"
  width="100%" height="640" style="border:0;border-radius:16px"
  allow="xr-spatial-tracking; camera; fullscreen; web-share; clipboard-write" loading="lazy"></iframe>

<!-- 2. JS SDK: auto-resize and cart events -->
<div id="ring"></div>
<script src="https://app.example.com/ring-builder.js"></script>
<script>
  RMJRingBuilder.mount('#ring', {
    slug: 'SLUG',
    onSelection: (s) => updatePrice(s.price),
    onAddToCart: (s) => cart.add(s),   // s = { band, shape, setting, carat, metal, sku, priceCents, currency, quoteId }
  });
</script>

Use cases

Shopify or WooCommerce
Listen for the addToCart event and create a cart line with the chosen specs and price.
Custom storefront
Embed with the JS SDK and read selection events to drive your own price and spec panel.
Headless
Read /ring-builder/catalog (with per-part and per-value SKU and price) and POST /ring-builder/quote from your backend for the composed SKU and total, with the build’s measured metal weight and stone count in specs.
Automation
Update your ring-builder embed’s config, allowed domains, price books, stone sets and Shopify catalog sync from your own backend over the REST API, and push each variant’s weight and stone specs to Shopify.

Events are sent with postMessage and source: "rmj-ring-builder". Domain locking is enforced with a per-embed frame-ancestors CSP; set embedOnly to also refuse opening the embed URL outside an iframe. Every option, callback and event: the JavaScript SDK reference.

GraphQL API

One endpoint for every customer feature. Ask for exactly the fields you need, with the same bearer keys, scopes and per-key domain lock as REST.

The rate limit counts requests, so one document may repeat a root field at most 25 times. The metered fields (ringConcierge, lifestyleShot) and pushShopifySpecs may appear once, as on REST. Aliases count. Each basket field after a document’s first counts as one more request. An operation may also nest at most 10 fields deep and select at most 1,000 fields.

Open the GraphQL playground
POST /api/graphql
# Ask for exactly the fields you need: projects, embeds, catalog, price, org
query {
  organization { plan creditsBalance }
  embeds(type: "ring_builder") { id embedUrl isActive }
  ringCatalog { groups { code label values { code label } } }
}

# Quote a build with its measured weight and accent stones
query {
  ringQuote(selection: { bandSlug: "classic-round", metalPresetId: "yellow_gold_18k" }) {
    sku priceCents currency
    specs { metalGrams sideStoneCount sideStoneCtw }
  }
}

# Create a ring-builder embed
mutation {
  createEmbed(input: {
    type: "ring_builder",
    allowedDomains: ["myshop.com"],
    config: { pricing: { enabled: true, currency: "USD", base: 1200, perCaratDelta: 800 } }
  }) { id embedUrl }
}

# curl:
# curl https://app.example.com/api/graphql \
#   -H "Authorization: Bearer rmj_live_…" -H "Content-Type: application/json" \
#   -d '{"query":"{ organization { plan creditsBalance } }"}'

Webhooks

Get leads, saved designs, carts and orders pushed to your CRM or backend the moment they happen: lead.created, design.saved, cart.created, order.created, order.attributed and order.paid. Each delivery is signed with your endpoint’s own secret, and you manage endpoints in the dashboard, over REST or in GraphQL.

REST endpoints

Authenticate with Authorization: Bearer rmj_live_…. The @3jd/sdk package wraps these with types for Node and the browser. A ringbuilder:read key also holds builder:read, and a ringbuilder:write key builder:write.

MethodPathDescriptionScope
GET/api/public/projectsList projects, each with its piece type; ?jewelryType= keeps one typemodels:read
POST/api/public/projectsCreate a project (idempotent)models:write
GET/api/public/projects/:idFetch a project, including its studio config, model check and display propmodels:read
PATCH/api/public/projects/:idUpdate the name, config or thumbnailmodels:write
POST/api/public/projects/bulkMove, trash, restore, tag or delete for good up to 200 designs at oncemodels:write
DELETE/api/public/projects/:idDelete a projectmodels:write
GET/api/public/projects/:id/versionsList a project's version historymodels:read
POST/api/public/projects/:id/versionsSnapshot a version (named or autosave)models:write
POST/api/public/projects/:id/versions/:vid/restoreRestore a project to a versionmodels:write
DELETE/api/public/projects/:id/versions/:vidDelete a version from the historymodels:write
POST/api/public/metal-weightMeasure a GLB: metal volume, weight at every alloy, and its stones (up to 4 MB; larger models via their project; Material Lab plan)models:read
POST/api/public/projects/:id/metal-weightThe same measure on a saved project's GLB modelmodels:read
GET/api/public/sharesList design-share links (/d/:slug)shares:read
POST/api/public/sharesCreate the share link for a projectshares:write
GET/api/public/shares/:idFetch a design shareshares:read
PATCH/api/public/shares/:idSet access, password, expiry and downloads, rotate the slug, or set viewer controls (viewer.tryOn.zones: which body zones try-on may open)shares:write
DELETE/api/public/shares/:idDelete a design shareshares:write
GET/api/public/creditsPlan and live credit balanceusage:read
GET/api/public/billing/receiptsWhat your account paid us (plan periods, credit packs, services), newest firstusage:read
GET/api/public/render-ordersYour render on demand orders: files, total, status and each order's pageservices:read
GET/api/public/render-orders/pricesRender on demand's price list per still and video quality (no key needed)public
GET/api/public/cad-documentsList your Jewelry CAD documents (Jewelry CAD plan); ?jewelryType= keeps one typemodels:read
GET/api/public/cad-templatesThe Jewelry CAD starter templates, each with the piece it starts and its document; ?jewelryType= keeps one typemodels:read
POST/api/public/cad-documentsCreate a CAD document from its parametric recipemodels:write
GET/api/public/cad-documents/:idFetch a CAD document with its parametric recipemodels:read
PATCH/api/public/cad-documents/:idRename or replace a CAD documentmodels:write
DELETE/api/public/cad-documents/:idDelete a CAD documentmodels:write
GET/api/public/studio/workspacesWhere your studio shows each workspace (Studio, Playground, Design) and each tool: enabled, locked or hiddenusage:read
GET/api/public/service-ordersYour done-for-you service orders and their statusservices:read
POST/api/public/service-ordersOrder a service: setup, CAD, photos or 3D (returns a checkout link)services:write
GET/api/public/account/exportEverything in your account as one JSON file: projects with their file links, embeds, shares, leads, orders, credits and team. Never a secretaccount:read
GET/api/public/account/businessYour business details and where our review of them stands: needed, sent, asked for more, verified or not approvedaccount:read
GET/api/public/account/jewelry-typesWhat your account sells: the jewelry types you chose, and each one's builder and try-on on your planaccount:read
PUT/api/public/account/jewelry-typesSet what your account sells (at least one type), or null to go back to ringsaccount:write
GET/api/public/webhooksYour webhook endpoints, never their secretswebhooks:read
POST/api/public/webhooksRegister an endpoint for lead, design, cart and order events; the signing secret is in this answer onlywebhooks:write
PATCH/api/public/webhooks/:idPause or resume an endpoint, or change its url or eventswebhooks:write
DELETE/api/public/webhooks/:idRemove a webhook endpointwebhooks:write
GET/api/public/ring-builder/catalogPublished ring catalog (groups, parts, bandOptions, made-to-match links, default). ?band=<slug> narrows it; ?includeTypes=1 adds wedding bands; 30 requests a minuteringbuilder:read
GET/api/public/ring-builder/embedsList ring-builder embedsringbuilder:read
POST/api/public/ring-builder/embedsCreate a ring-builder embedringbuilder:write
PATCH/api/public/ring-builder/embeds/:idUpdate the config (incl. tryOn.zones), domains or active stateringbuilder:write
POST/api/public/ring-builder/embeds/:id/duplicateClone an embed with its price books, stone sets and pageringbuilder:write
GET/api/public/ring-builder/embeds/:id/shopify-catalogShopify catalog sync settings and the last run's reportringbuilder:read
PATCH/api/public/ring-builder/embeds/:id/shopify-catalogUpdate the Shopify catalog sync (prefix, collection, mode)ringbuilder:write
GET/api/public/ring-builder/embeds/:id/shopify-catalog/render-profileHow Shopify product photos are shot: the profile, what it inherits and the resolved resultringbuilder:read
PUT/api/public/ring-builder/embeds/:id/shopify-catalog/render-profileReplace the photo profile (camera, output, lighting, stage, engraving)ringbuilder:write
GET/api/public/ring-builder/embeds/:id/stone-feedA builder's diamond feed: the partner feed, your own supplier, or both (the key is never returned)ringbuilder:read
PATCH/api/public/ring-builder/embeds/:id/stone-feedChoose a builder's diamond source, your supplier's address and key, and its markupringbuilder:write
POST/api/public/ring-builder/embeds/:id/stone-feed/testRead a builder's own diamond feed now: how many stones it recognised, or why it could notringbuilder:write
GET/api/public/ring-builder/embeds/:id/diamonds/alternativesLive diamonds close to one that has sold: the same shape, a carat and a price near itringbuilder:read
POST/api/public/ring-builder/embeds/:id/shopify-specsPush each variant's exact metal weight and accent-stone specs to Shopify as metafields, and as shipping weight if you ask. While more is true, call again with nextCursor as cursor. Turned off for now: it answers 423ringbuilder:write
GET/api/public/ring-builder/pricingAn embed's price books and per-part overrides, plus its own cost-pricing overrides (price per gram by alloy, accent-stone tiers)ringbuilder:read
POST/api/public/ring-builder/pricingCreate or update an embed's price book (currency, markup, rounding, how it prices: list, formula or Shopify) and its cost-pricing overridesringbuilder:write
GET/api/public/ring-builder/pricing/combosA price book's price for each combination (a design in a metal at a carat)ringbuilder:read
PUT/api/public/ring-builder/pricing/combosSave combination prices, up to 2000 at once (replace to remove the rest)ringbuilder:write
DELETE/api/public/ring-builder/pricing/combosRemove combination prices by key, or all of themringbuilder:write
GET/api/public/ring-builder/pricing/combos/gridOne design's combinations with today's price and yours, as JSON or a CSV sheetringbuilder:read
GET/api/public/ring-builder/pricing/weight-checkThe settings and bands Price from weight can't price yet (not measured, not watertight, centre stone unclear), each with its reasonringbuilder:read
GET/api/public/ring-builder/stone-setsAn embed's centre-stone tiersringbuilder:read
POST/api/public/ring-builder/stone-setsCreate a centre-stone tierringbuilder:write
GET/api/public/ring-builder/pagesAn embed's designed page: the draft and the live versionringbuilder:read
POST/api/public/ring-builder/pagesSave the page draft, and optionally publish or unpublish itringbuilder:write
GET/api/public/ring-builder/embeds/:id/themesA builder's themes (layout, colours and type, the 3D look, a designed page), the live one markedringbuilder:read
POST/api/public/ring-builder/embeds/:id/themesCreate a theme from the live look, another theme, a release or the defaults; never live until publishedringbuilder:write
PATCH/api/public/ring-builder/embeds/:id/themes/:themeIdSave a theme's draft look and page, or put a ready-made layout in it with { layout } (ifUpdatedAt keeps two editors from overwriting each other)ringbuilder:write
GET/api/public/ring-builder/layoutsThe six ready-made layouts (Three steps, One page, Diamond first, Product page, Showcase, Sidebar) and the structure each setsringbuilder:read
DELETE/api/public/ring-builder/embeds/:id/themes/:themeIdDelete a theme that is not liveringbuilder:write
POST/api/public/ring-builder/embeds/:id/themes/:themeId/previewA 7-day preview link: the theme's draft on your store page with ?3jd_theme=, nothing live (DELETE revokes them)ringbuilder:write
GET/api/public/ring-builder/embeds/:id/host-looksMatch my store: the store pages the builder has run on and the colours, fonts and buttons it saw; create a theme from one with from: { hostLook }, or set appearance.storeLook to auto and the builder takes them by itselfringbuilder:read
GET/api/public/ring-builder/embeds/:id/readinessThe launch checklist: store, default ring, steps, prices, diamonds, cart, on your store, test mode and live, each with what fixes itringbuilder:read
PUT/api/public/ring-builder/embeds/:id/draftSave the settings draft (the full config), never live until publishedringbuilder:write
GET/api/public/ring-builder/embeds/:id/showroom-pinWhether the in-store showroom's staff PIN is set (never the PIN)ringbuilder:read
PUT/api/public/ring-builder/embeds/:id/showroom-pinSet the showroom staff PIN (4 to 8 digits), or remove it with null; ends every staff sessionringbuilder:write
POST/api/public/ring-builder/embeds/:id/publishPublish the settings draft and a theme together, keeping a release; dryRun lists the changesringbuilder:write
GET/api/public/ring-builder/embeds/:id/releasesEvery publish, newest first; POST { releaseId } loads one back into the draftsringbuilder:read
POST/api/public/ring-builder/priceServer-calculated price for a selectionringbuilder:read
POST/api/public/ring-builder/quoteComposed SKU and total (sum-of-parts catalog pricing), plus the build's measured metal weight and accent stones as specsringbuilder:read
POST/api/public/ring-builder/compareUp to four rings side by side, priced, with the differing details markedringbuilder:read
POST/api/public/ring-builder/conciergeAI concierge: a chat turn returns a validated selection and its server price. Uses credits. The ringbuilder:read scope is deprecated here and stops working on 2026-12-01ringbuilder:write
POST/api/public/ring-builder/cartAdd a configured ring to the storefront cart (match, mint, draft, hosted, event)commerce:write
POST/api/public/ring-builder/verifyTurn a signed quote token back into the price it proves. No key needed: the token is the credentialpublic
POST/api/public/builder/basket/quotePrice a basket (a ring with its matching bands, or several pieces) with the store's set discount, signedbuilder:read
POST/api/public/builder/basket/cartAdd a whole basket to the storefront cart: every line, one draft order or one hosted checkout, never part of a set. A set with a discount goes as one draft order or one checkout. A commerce:write key works toobuilder:write
POST/api/public/builder/basket/requestSend a basket to the store as an order request: one lead with every piecebuilder:write
GET/api/public/diamonds/searchSearch the live loose-diamond feed (retail prices, marked up)diamonds:read
GET/api/public/diamonds/sizeA diamond's face-up size in mm by shape and carat (or its measurements), as the carat sizer draws itdiamonds:read
GET/api/public/ordersOrders paid through your builders' own checkout, with every piece (then /:id)orders:read
GET/api/public/catalog/designsYour own designs, each band with its heads (Your Own Designs on the plan)catalog:read
GET/api/public/catalog/componentsEvery part of your own designs, drafts includedcatalog:read
POST/api/public/catalog/filesUpload a GLB model or a picture of your own designs (large files in parts)catalog:write
POST/api/public/catalog/componentsPublish one of your own parts from uploaded filescatalog:write
PATCH/api/public/catalog/components/:idChange one of your own parts (then DELETE to remove it, PUT /files for a new model)catalog:write
GET/api/public/catalog/design-linksYour made-to-match links (then POST to link, PATCH or DELETE /:id)catalog:read
GET/api/public/integrations/shopifyShopify connection status and attributed order revenueintegrations:read
PATCH/api/public/integrations/shopifySay whether builders may add products to your Shopify store at an add to cartringbuilder:write
POST/api/public/lifestyleStage a ring render into a branded lifestyle scenelifestyle:write
POST/api/public/checkout/sessionCreate a checkout for a ring selection (priced on the server), paid to your own PayPalcheckout:write
GET/api/public/ring-builder/quotesList saved quotesringbuilder:read
POST/api/public/ring-builder/quotesSave a quote or enquiryringbuilder:write
GET/api/public/ring-builder/quotes/:idRetrieve a saved quoteringbuilder:read
PATCH/api/public/ring-builder/quotes/:idUpdate a lead's status and notesringbuilder:write
DELETE/api/public/ring-builder/quotes/:idErase a shopper's personal data on request: their leads, and the email and engraving on their ordersringbuilder:write
GET/api/public/ring-builder/quotes/:id/priceWhat a lead's ring costs today on the same builderringbuilder:read
GET/api/public/ring-builder/quotes/:id/specA lead's order spec sheet as data, for the workshopringbuilder:read
GET/api/public/embedsYour viewer embeds: each saved design published as an interactive 3D viewer, with its project's nameembeds:read
POST/api/public/embedsPublish one of your projects as a viewer embed, within your plan's embedsembeds:write
GET/api/public/embeds/:id/analyticsEmbed analytics: funnel, daily trend and top selectionsanalytics:read
GET/api/public/contentEvery published article, answer, doc, glossary term and comparisonpublic
GET/api/public/content/:kindPublished content of one kindpublic
GET/api/public/content/:kind/:slugOne document, including its raw MDX bodypublic

Create your first API key

API access is included on the Pro, Advanced, and Enterprise plans. Create a key in the dashboard and choose its scopes.