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.
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).
<!-- 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
addToCartevent and create a cart line with the chosen specs and price. - Custom storefront
- Embed with the JS SDK and read
selectionevents 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/quotefrom your backend for the composed SKU and total, with the build’s measured metal weight and stone count inspecs. - 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.
# 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.
| Method | Path | Description | Scope |
|---|---|---|---|
| GET | /api/public/projects | List projects, each with its piece type; ?jewelryType= keeps one type | models:read |
| POST | /api/public/projects | Create a project (idempotent) | models:write |
| GET | /api/public/projects/:id | Fetch a project, including its studio config, model check and display prop | models:read |
| PATCH | /api/public/projects/:id | Update the name, config or thumbnail | models:write |
| POST | /api/public/projects/bulk | Move, trash, restore, tag or delete for good up to 200 designs at once | models:write |
| DELETE | /api/public/projects/:id | Delete a project | models:write |
| GET | /api/public/projects/:id/versions | List a project's version history | models:read |
| POST | /api/public/projects/:id/versions | Snapshot a version (named or autosave) | models:write |
| POST | /api/public/projects/:id/versions/:vid/restore | Restore a project to a version | models:write |
| DELETE | /api/public/projects/:id/versions/:vid | Delete a version from the history | models:write |
| POST | /api/public/metal-weight | Measure 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-weight | The same measure on a saved project's GLB model | models:read |
| GET | /api/public/shares | List design-share links (/d/:slug) | shares:read |
| POST | /api/public/shares | Create the share link for a project | shares:write |
| GET | /api/public/shares/:id | Fetch a design share | shares:read |
| PATCH | /api/public/shares/:id | Set 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/:id | Delete a design share | shares:write |
| GET | /api/public/credits | Plan and live credit balance | usage:read |
| GET | /api/public/billing/receipts | What your account paid us (plan periods, credit packs, services), newest first | usage:read |
| GET | /api/public/render-orders | Your render on demand orders: files, total, status and each order's page | services:read |
| GET | /api/public/render-orders/prices | Render on demand's price list per still and video quality (no key needed) | public |
| GET | /api/public/cad-documents | List your Jewelry CAD documents (Jewelry CAD plan); ?jewelryType= keeps one type | models:read |
| GET | /api/public/cad-templates | The Jewelry CAD starter templates, each with the piece it starts and its document; ?jewelryType= keeps one type | models:read |
| POST | /api/public/cad-documents | Create a CAD document from its parametric recipe | models:write |
| GET | /api/public/cad-documents/:id | Fetch a CAD document with its parametric recipe | models:read |
| PATCH | /api/public/cad-documents/:id | Rename or replace a CAD document | models:write |
| DELETE | /api/public/cad-documents/:id | Delete a CAD document | models:write |
| GET | /api/public/studio/workspaces | Where your studio shows each workspace (Studio, Playground, Design) and each tool: enabled, locked or hidden | usage:read |
| GET | /api/public/service-orders | Your done-for-you service orders and their status | services:read |
| POST | /api/public/service-orders | Order a service: setup, CAD, photos or 3D (returns a checkout link) | services:write |
| GET | /api/public/account/export | Everything in your account as one JSON file: projects with their file links, embeds, shares, leads, orders, credits and team. Never a secret | account:read |
| GET | /api/public/account/business | Your business details and where our review of them stands: needed, sent, asked for more, verified or not approved | account:read |
| GET | /api/public/account/jewelry-types | What your account sells: the jewelry types you chose, and each one's builder and try-on on your plan | account:read |
| PUT | /api/public/account/jewelry-types | Set what your account sells (at least one type), or null to go back to rings | account:write |
| GET | /api/public/webhooks | Your webhook endpoints, never their secrets | webhooks:read |
| POST | /api/public/webhooks | Register an endpoint for lead, design, cart and order events; the signing secret is in this answer only | webhooks:write |
| PATCH | /api/public/webhooks/:id | Pause or resume an endpoint, or change its url or events | webhooks:write |
| DELETE | /api/public/webhooks/:id | Remove a webhook endpoint | webhooks:write |
| GET | /api/public/ring-builder/catalog | Published ring catalog (groups, parts, bandOptions, made-to-match links, default). ?band=<slug> narrows it; ?includeTypes=1 adds wedding bands; 30 requests a minute | ringbuilder:read |
| GET | /api/public/ring-builder/embeds | List ring-builder embeds | ringbuilder:read |
| POST | /api/public/ring-builder/embeds | Create a ring-builder embed | ringbuilder:write |
| PATCH | /api/public/ring-builder/embeds/:id | Update the config (incl. tryOn.zones), domains or active state | ringbuilder:write |
| POST | /api/public/ring-builder/embeds/:id/duplicate | Clone an embed with its price books, stone sets and page | ringbuilder:write |
| GET | /api/public/ring-builder/embeds/:id/shopify-catalog | Shopify catalog sync settings and the last run's report | ringbuilder:read |
| PATCH | /api/public/ring-builder/embeds/:id/shopify-catalog | Update the Shopify catalog sync (prefix, collection, mode) | ringbuilder:write |
| GET | /api/public/ring-builder/embeds/:id/shopify-catalog/render-profile | How Shopify product photos are shot: the profile, what it inherits and the resolved result | ringbuilder:read |
| PUT | /api/public/ring-builder/embeds/:id/shopify-catalog/render-profile | Replace the photo profile (camera, output, lighting, stage, engraving) | ringbuilder:write |
| GET | /api/public/ring-builder/embeds/:id/stone-feed | A 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-feed | Choose a builder's diamond source, your supplier's address and key, and its markup | ringbuilder:write |
| POST | /api/public/ring-builder/embeds/:id/stone-feed/test | Read a builder's own diamond feed now: how many stones it recognised, or why it could not | ringbuilder:write |
| GET | /api/public/ring-builder/embeds/:id/diamonds/alternatives | Live diamonds close to one that has sold: the same shape, a carat and a price near it | ringbuilder:read |
| POST | /api/public/ring-builder/embeds/:id/shopify-specs | Push 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 423 | ringbuilder:write |
| GET | /api/public/ring-builder/pricing | An 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/pricing | Create or update an embed's price book (currency, markup, rounding, how it prices: list, formula or Shopify) and its cost-pricing overrides | ringbuilder:write |
| GET | /api/public/ring-builder/pricing/combos | A price book's price for each combination (a design in a metal at a carat) | ringbuilder:read |
| PUT | /api/public/ring-builder/pricing/combos | Save combination prices, up to 2000 at once (replace to remove the rest) | ringbuilder:write |
| DELETE | /api/public/ring-builder/pricing/combos | Remove combination prices by key, or all of them | ringbuilder:write |
| GET | /api/public/ring-builder/pricing/combos/grid | One design's combinations with today's price and yours, as JSON or a CSV sheet | ringbuilder:read |
| GET | /api/public/ring-builder/pricing/weight-check | The settings and bands Price from weight can't price yet (not measured, not watertight, centre stone unclear), each with its reason | ringbuilder:read |
| GET | /api/public/ring-builder/stone-sets | An embed's centre-stone tiers | ringbuilder:read |
| POST | /api/public/ring-builder/stone-sets | Create a centre-stone tier | ringbuilder:write |
| GET | /api/public/ring-builder/pages | An embed's designed page: the draft and the live version | ringbuilder:read |
| POST | /api/public/ring-builder/pages | Save the page draft, and optionally publish or unpublish it | ringbuilder:write |
| GET | /api/public/ring-builder/embeds/:id/themes | A builder's themes (layout, colours and type, the 3D look, a designed page), the live one marked | ringbuilder:read |
| POST | /api/public/ring-builder/embeds/:id/themes | Create a theme from the live look, another theme, a release or the defaults; never live until published | ringbuilder:write |
| PATCH | /api/public/ring-builder/embeds/:id/themes/:themeId | Save 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/layouts | The six ready-made layouts (Three steps, One page, Diamond first, Product page, Showcase, Sidebar) and the structure each sets | ringbuilder:read |
| DELETE | /api/public/ring-builder/embeds/:id/themes/:themeId | Delete a theme that is not live | ringbuilder:write |
| POST | /api/public/ring-builder/embeds/:id/themes/:themeId/preview | A 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-looks | Match 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 itself | ringbuilder:read |
| GET | /api/public/ring-builder/embeds/:id/readiness | The launch checklist: store, default ring, steps, prices, diamonds, cart, on your store, test mode and live, each with what fixes it | ringbuilder:read |
| PUT | /api/public/ring-builder/embeds/:id/draft | Save the settings draft (the full config), never live until published | ringbuilder:write |
| GET | /api/public/ring-builder/embeds/:id/showroom-pin | Whether the in-store showroom's staff PIN is set (never the PIN) | ringbuilder:read |
| PUT | /api/public/ring-builder/embeds/:id/showroom-pin | Set the showroom staff PIN (4 to 8 digits), or remove it with null; ends every staff session | ringbuilder:write |
| POST | /api/public/ring-builder/embeds/:id/publish | Publish the settings draft and a theme together, keeping a release; dryRun lists the changes | ringbuilder:write |
| GET | /api/public/ring-builder/embeds/:id/releases | Every publish, newest first; POST { releaseId } loads one back into the drafts | ringbuilder:read |
| POST | /api/public/ring-builder/price | Server-calculated price for a selection | ringbuilder:read |
| POST | /api/public/ring-builder/quote | Composed SKU and total (sum-of-parts catalog pricing), plus the build's measured metal weight and accent stones as specs | ringbuilder:read |
| POST | /api/public/ring-builder/compare | Up to four rings side by side, priced, with the differing details marked | ringbuilder:read |
| POST | /api/public/ring-builder/concierge | AI 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-01 | ringbuilder:write |
| POST | /api/public/ring-builder/cart | Add a configured ring to the storefront cart (match, mint, draft, hosted, event) | commerce:write |
| POST | /api/public/ring-builder/verify | Turn a signed quote token back into the price it proves. No key needed: the token is the credential | public |
| POST | /api/public/builder/basket/quote | Price a basket (a ring with its matching bands, or several pieces) with the store's set discount, signed | builder:read |
| POST | /api/public/builder/basket/cart | Add 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 too | builder:write |
| POST | /api/public/builder/basket/request | Send a basket to the store as an order request: one lead with every piece | builder:write |
| GET | /api/public/diamonds/search | Search the live loose-diamond feed (retail prices, marked up) | diamonds:read |
| GET | /api/public/diamonds/size | A diamond's face-up size in mm by shape and carat (or its measurements), as the carat sizer draws it | diamonds:read |
| GET | /api/public/orders | Orders paid through your builders' own checkout, with every piece (then /:id) | orders:read |
| GET | /api/public/catalog/designs | Your own designs, each band with its heads (Your Own Designs on the plan) | catalog:read |
| GET | /api/public/catalog/components | Every part of your own designs, drafts included | catalog:read |
| POST | /api/public/catalog/files | Upload a GLB model or a picture of your own designs (large files in parts) | catalog:write |
| POST | /api/public/catalog/components | Publish one of your own parts from uploaded files | catalog:write |
| PATCH | /api/public/catalog/components/:id | Change one of your own parts (then DELETE to remove it, PUT /files for a new model) | catalog:write |
| GET | /api/public/catalog/design-links | Your made-to-match links (then POST to link, PATCH or DELETE /:id) | catalog:read |
| GET | /api/public/integrations/shopify | Shopify connection status and attributed order revenue | integrations:read |
| PATCH | /api/public/integrations/shopify | Say whether builders may add products to your Shopify store at an add to cart | ringbuilder:write |
| POST | /api/public/lifestyle | Stage a ring render into a branded lifestyle scene | lifestyle:write |
| POST | /api/public/checkout/session | Create a checkout for a ring selection (priced on the server), paid to your own PayPal | checkout:write |
| GET | /api/public/ring-builder/quotes | List saved quotes | ringbuilder:read |
| POST | /api/public/ring-builder/quotes | Save a quote or enquiry | ringbuilder:write |
| GET | /api/public/ring-builder/quotes/:id | Retrieve a saved quote | ringbuilder:read |
| PATCH | /api/public/ring-builder/quotes/:id | Update a lead's status and notes | ringbuilder:write |
| DELETE | /api/public/ring-builder/quotes/:id | Erase a shopper's personal data on request: their leads, and the email and engraving on their orders | ringbuilder:write |
| GET | /api/public/ring-builder/quotes/:id/price | What a lead's ring costs today on the same builder | ringbuilder:read |
| GET | /api/public/ring-builder/quotes/:id/spec | A lead's order spec sheet as data, for the workshop | ringbuilder:read |
| GET | /api/public/embeds | Your viewer embeds: each saved design published as an interactive 3D viewer, with its project's name | embeds:read |
| POST | /api/public/embeds | Publish one of your projects as a viewer embed, within your plan's embeds | embeds:write |
| GET | /api/public/embeds/:id/analytics | Embed analytics: funnel, daily trend and top selections | analytics:read |
| GET | /api/public/content | Every published article, answer, doc, glossary term and comparison | public |
| GET | /api/public/content/:kind | Published content of one kind | public |
| GET | /api/public/content/:kind/:slug | One document, including its raw MDX body | public |