A 3D product configurator API should keep one resolved configuration consistent across the interface, 3D scene, pricing service, saved design, cart and order. The browser may request a change, but the product rules and commerce services decide whether that change is valid and what it costs. The cart should receive a stable configuration reference, not trust whatever the canvas happens to show.
Build the integration around four explicit contracts: commands describe what a caller wants to change; resolved state records what is currently valid; domain events report accepted facts; commerce operations revalidate the state before price or cart commitment. Give each contract its own schema and version. This prevents an option click, a visual update and a completed cart action from being treated as the same event.
Treat resolved state as the product contract
The resolved state is the smallest record that can answer, "What product has this buyer configured, under which rules, at which revision?" It needs stable product and option identifiers, the selected values, calculated values that affect validity, a rule-set version, a configuration revision and a lifecycle status. Labels, thumbnails and mesh names can be projections of that record, but they should not replace it.
The broader 3D product configurator e-commerce integration blueprint covers product data, cart, order, analytics and production handoff as a whole. This guide stays at the API boundary: how those systems exchange intent, accepted state and evidence without becoming competing sources of truth.
Start by naming the authority for each field. A product information system may own public names and descriptions. A rules service may own compatibility. A pricing service may own a market-specific amount. The 3D runtime may own camera position, but it should not quietly become the authority for a sellable option. If the experience changes the product itself rather than only choosing which content a visitor sees, the distinction in e-commerce personalization versus product customization is worth settling before the API is designed.
Separate commands, state and events
Commands are requests. Use imperative names such as change-option, set-dimension, save-configuration and add-configuration-to-cart. A command can fail because the caller is unauthorized, the revision is stale, the option is incompatible or a required input is missing.
Events are accepted facts. Use past-tense names such as configuration.option-changed, configuration.validated, configuration.saved, price.resolved and cart.line-created. Do not publish option-changed merely because a control was clicked. Publish it after the rule service has accepted the change and assigned the resulting revision. The taxonomy below is Double Edged's editorial architecture, not a vendor standard.

| Family | Example names | What the record means | Typical consumers |
|---|---|---|---|
| Lifecycle | configurator.ready, configuration.created, configuration.loaded | A runtime or configuration reached a known lifecycle point | Host page, support tooling, observability |
| Configuration domain | configuration.option-changed, configuration.invalidated, configuration.saved | Product state changed or failed a named rule under a known revision | UI, 3D adapter, save service, audit trail |
| Commerce | price.resolved, price.expired, cart.line-created, cart.line-rejected | A commerce service accepted or rejected a versioned state | Storefront, cart, quote service, order adapter |
| Operational | asset.load-failed, integration.unavailable, request.rate-limited | A technical dependency affected the experience | Monitoring, support, fallback logic |
| Analytics | configurator_started, option_selected, configuration_completed | A measurement event was emitted for an agreed behavior | Analytics pipeline only; never product-rule authority |
A current vendor API will not necessarily use these names. As checked on 31 August 2026, Vectary's Model API documentation describes two-way website-to-scene events and connects event values to variables by name; if no matching variable exists, the documented result is undefined.[2] That is a useful integration surface, but it is also why a vendor adapter should translate scene events into your own versioned domain contract instead of letting a scene variable become a cart field by accident.
Give every event a versioned envelope
CloudEvents 1.0.2 is a vendor-neutral event format whose required context attributes are id, source, specversion and type.[1] A configurator does not have to adopt CloudEvents, but those fields are a good minimum for an event that must cross a viewer, storefront and backend.
Keep the envelope and project payload separate. In this example, seven project-defined identifiers sit inside data: configuration_id, configuration_revision, product_id, product_model_version, rule_set_version, correlation_id and causation_id. The schema named by dataschema should define every field. It should also say whether causation_id points to the command that produced the event or to an earlier event. changes carries the accepted change. These names belong to this example contract; the CloudEvents citation supports only the envelope attributes it names.[1]
This illustrative event uses the fields below. Dots show nesting, and the numeric segment marks the first item in the changes array.
| Field path | Example value | Meaning in this example |
|---|---|---|
| id | evt_01J_EXAMPLE | Unique event identifier |
| type | com.example.configuration.option-changed.v1 | Versioned event type |
| source | /configurator/storefront | Event producer context |
| subject | config/cfg_2048/revision/12 | Configuration and revision addressed by the event |
| specversion | 1.0 | CloudEvents specification version used by the envelope |
| time | 2026-08-31T00:00:00Z | Example occurrence time |
| dataschema | https://api.example.com/schemas/option-changed/v1 | Schema for the data object |
| data | Object | Project-defined event payload |
| data.configuration_id | cfg_2048 | Stable configuration identifier |
| data.configuration_revision | 12 | Accepted configuration revision |
| data.product_id | product_310 | Stable product identifier |
| data.product_model_version | 2026.08.31 | Product-model version used to resolve the state |
| data.rule_set_version | 2026.08 | Rule-set version used to validate the change |
| data.correlation_id | session_84c | Project-defined correlation identifier |
| data.causation_id | cmd_01J_EXAMPLE | Project-defined identifier for the direct cause |
| data.changes | One-item array | Accepted changes carried by this event |
| data.changes.0.option_id | finish | Changed option identifier |
| data.changes.0.value_id | oxide-blue | Accepted value identifier |
CloudEvents says an incompatible data-schema change should use a different dataschema URI. It also warns that sensitive information should not be carried in context attributes because intermediaries may inspect or log them.[1] Keep email addresses, free-text personalization and access tokens out of event metadata.
Validate envelopes and command payloads before they reach business logic. JSON Schema's 2020-12 validation vocabulary can assert structure with keywords such as type, enum and required.[3] Schema validation catches malformed structure; the rule engine still has to decide whether a structurally valid choice is allowed for this product and revision.
Follow one commerce-safe sample sequence
The sequence below keeps request, accepted state and commerce evidence separate. Endpoint names are illustrative. The useful part is the ownership and revision behavior.

| Step | Caller and operation | Accepted result | Evidence to retain |
|---|---|---|---|
| 1. Load | Host requests product definition and configuration capabilities | Product definition, option IDs, rule-set version and API capabilities | Product and contract versions used to start the session |
| 2. Create | Host sends POST /configurations with product and market context | configuration.created at revision 1 | Configuration ID, owner class, market and expiry policy |
| 3. Change | UI sends change-option with expected revision 1 | Accepted state at revision 2, or a typed rejection | Command ID, prior revision, changed IDs and rule outcome |
| 4. Project | 3D adapter receives revision 2 and updates materials, parts or dimensions | Visual acknowledgement for revision 2 | Asset-map version and any visible fallback or load failure |
| 5. Price | Storefront requests a price for configuration revision 2 | price.resolved with a price reference, currency and expiry | Pricing authority, configuration revision and price reference |
| 6. Save | Save service writes the validated snapshot conditionally | configuration.saved with a durable ID and revision | Ownership, visibility, schema versions and updated timestamp |
| 7. Add to cart | Storefront submits configuration ID, revision, quantity and an idempotency key | cart.line-created, or a typed rejection after revalidation | Cart-line ID, accepted configuration revision and price reference |
| 8. Measure | Analytics adapter receives accepted domain and commerce events | Approved analytics events | Consent state, event version and non-sensitive identifiers |
If two tabs start from revision 2, the first accepted write may produce revision 3. The second write must not overwrite it. Choose one concurrency contract and document it.
For an HTTP ETag mapping, return a strong ETag for the configuration representation and require If-Match on state-changing requests.[9] Use 428 Precondition Required if the client omits that required condition; RFC 6585 defines 428 as the origin requiring the request to be conditional and shows If-Match in its example.[4] When If-Match is present but its entity tag is stale, RFC 9110 says the origin must not perform the method and may return 412 Precondition Failed.[9] Return a stable problem code and, when authorization allows it, the current revision so the client can reload, compare or reapply the change.
An expected_revision field carried only in the JSON body is different. Document it as an application-level revision check, not an HTTP precondition.[9]
The estimator maps rules, assets, visualization, integrations, pricing, outputs and operating ownership into an explainable complexity profile. It does not generate a supplier quote; price and timing still depend on verified requirements and acceptance criteria.
Persist identity, revisions and ownership
A saved configuration needs more than a JSON blob. Store a stable configuration ID and immutable revision number, then record the product definition, rule set, selected option IDs, calculated values, asset mapping, market and currency context, lifecycle status and ownership. Keep a readable summary alongside the structured state so service and production teams can understand the order without reopening the original browser session.
Choose the lifecycle deliberately. A useful project-specific set might be draft, validated, priced, ordered, expired and archived. Those names are not universal. What matters is that the backend checks allowed transitions. A cart endpoint should not accept an expired draft merely because the frontend called the steps in the expected order.
In guidance checked on 31 August 2026, OWASP calls out out-of-order API execution and recommends server-side workflow-state validation rather than relying on frontend sequencing.[6] Apply that rule when an anonymous configuration is claimed by an account, when a saved design becomes a quote, and when a quoted revision becomes an order. Record who may read, change, share or duplicate each state.
A share URL should carry an opaque ID and, when the use case needs reproducibility, a revision. Do not put the full option payload, access token or customer text in the URL. A saved ID also gives the system somewhere to attach migrations when a rule set or product model changes.
Keep pricing and cart authority on the server
The 3D scene may display a price, but it should display a server result tied to a configuration revision. A price response should identify currency, market, quantity assumptions, tax treatment if relevant, expiry and the authority that calculated it. When any price-bearing choice changes, mark the old result stale rather than subtracting or adding browser-side deltas to an authoritative total.
At cart time, revalidate product availability, rule compatibility, revision and price. Store the configuration ID and accepted revision on the cart line, plus a human-readable option summary and the reference needed to reconstruct the price decision. Use an idempotency key for cart creation so a retry does not create a second line unintentionally; define the key's scope and retention in the contract.
Return errors as data the client can act on. RFC 9457 defines a machine-readable Problem Details format for HTTP APIs and the application/problem+json media type.[7] A configurator can extend that object with stable codes and safe fields for invalid_option, stale_revision, price_expired, not_authorized or cart_mapping_failed. The RFC also warns that problem details are not a debugging channel; do not expose stack traces, internal queries or secrets in detail.[7]
If pricing depends on guided selling, account terms, approval or quote-to-order work, the scope may have crossed into the territory covered by 3D product configuration software versus visual CPQ. Keep the configurator API responsible for its state contract rather than hiding an entire CPQ workflow inside one getPrice() call.
Secure the embed and API boundary
An embedded viewer often communicates with its host page through window.postMessage(). MDN recommends an exact targetOrigin, not *, when the receiver is known; it also says receivers should verify origin, possibly source, and the syntax of incoming messages.[5] Treat that check as the first gate, not as product authorization.
After origin and schema validation, the API still needs to verify that the caller may read or change this configuration. OWASP recommends HTTPS for secure REST endpoints and access control at every non-public endpoint.[6] Use short-lived, appropriately scoped credentials for browser calls. Keep server credentials out of scene variables, URLs and client bundles.
Define limits for payload size, command frequency and expensive operations such as dynamic geometry or generated documents. RFC 6585 defines 429 Too Many Requests and permits a Retry-After header.[4] A client should back off without discarding its local draft, while the server preserves enough correlation data to diagnose repeated failures without logging sensitive option content.
Keep analytics downstream of accepted state
Analytics should observe the product contract, not drive it. Emit a configuration event only after the corresponding command is accepted. Emit an add-to-cart measurement only after the cart service creates the line, not when a button receives a click.
Google's GA4 ecommerce documentation, last updated 19 August 2026, defines recommended events including add_to_cart, view_cart, begin_checkout and purchase, with products represented in an items array.[8] Map accepted configurator events into that commerce vocabulary where the semantics match. Keep configurator-specific diagnostics in a separate, documented namespace.
Send the minimum useful identifiers. A product ID, configuration type, market and revision class may be enough for operational analysis. Free-text engraving, account pricing, email addresses and private saved-design content do not belong in a general analytics payload.
Version the contracts independently
A single /v2/ route cannot describe every kind of change. Track the API contract version, event schema, product definition, rule set and asset mapping separately. A material-name change may only affect the 3D adapter. A retired option may require a saved-state migration. A pricing-rule change may leave the visual state untouched but invalidate an earlier price reference.
Prefer additive event changes when consumers can safely ignore new optional fields. Give breaking payloads a new schema identifier and test old and new consumers during a defined transition. Keep adapters at vendor boundaries: the storefront should not need to know whether a viewer calls a value selected_color, materialId or something else.
Document support windows and failure behavior. If an old saved revision cannot migrate automatically, return a clear state that preserves the original evidence and asks for review. Quietly replacing an unavailable component with the nearest current option changes the product and can turn a migration shortcut into an order error.
Test failure paths before launch

Run the contract against a common product, a rule-heavy product and one awkward exception. Keep the expected API, visual, price, cart and audit outcomes together.
- Send a valid option command and confirm that state, 3D and summary use the same accepted revision.
- Send an incompatible option and confirm that no price or cart event reports success.
- Replay the same command ID and verify the documented idempotent result.
- If the HTTP mapping requires If-Match, submit one write without it and another with a stale validator; verify separate 428 and 412 recovery paths.
- Try to open and edit another user's private saved configuration.
- Send a valid-looking postMessage from the wrong origin and from the wrong frame.
- Expire the price reference between configuration and cart creation.
- Remove or retire a selected option after a design has been saved.
- Make the 3D asset fail while product state remains valid, then verify the visible fallback.
- Repeat a cart request after a network timeout and confirm that the accepted result is not duplicated.
- Upgrade one event consumer while another remains on the prior compatible schema.
- Inspect analytics and logs for private text, credentials and unbounded payloads.
Record pass, fail and workaround separately. A manual correction on every order is operating work, not an API pass. A visually correct scene paired with an unrecoverable cart reference is also a failure.
Decide what belongs in discovery
The reusable pattern is simple: commands request, rules resolve, events report, commerce revalidates and persistent revisions preserve evidence. The project-specific work lies in the product rules, pricing authority, account model, downstream systems, security boundary, asset behavior and order output. Those details cannot be inferred from a generic API diagram.
Spellexo is relevant as Double Edged's owned-product proof of product strategy, UX, 3D production, engineering, testing, launch and operation. It is not proof of a configurator rescue, migration, conversion lift, return reduction or a prospect's integration outcome.
When the representative-product test exposes a genuine custom integration, the custom 3D product configurator service is the commercial route for discovery and implementation. Bring the state fields, event consumers, pricing authority, commerce platform, authentication model and required order record. That is enough to turn an API discussion into a bounded technical scope.





