logo

API

1. Overview

The Orbital Vision 3D Configurator is a self-contained application embedded inside an <iframe>. The parent application can communicate with it to:

  • Load or switch between products or ranges.
  • Select product options (e.g., fabrics, legs, sizes).
  • View or hide product dimensions.
  • Enter AR or VR modes (where supported).
  • Listen for updates (e.g., current price, SKU, etc.).

Communication is done via the standard Window.postMessage API.


2. iFrame URL Requirements

When embedding the configurator, you must include:

  1. Your API key, which authenticates the request.

You can create a new API key in your dashboard at app.orbital.vision/auth/api-keys. Please select the Product Configurator Access type from the dropdown list.

  1. Either a product ID or a range ID.

You can find your desired product ID or range ID by going to app.orbital.vision/products. On the lefthand side of the table there is an ID field for both ranges and products.

This requirement gives two possible URL structures:

  1. Product-Based
  2. Range-Based
  3. Authorised Domain

The [API_KEY] ensures that only authorised integrations can load the 3D environment. Without it, the server hosting the configurator will reject the request.

Required <iframe> Attributes

To enable a full range of features-particularly WebXR (AR/VR) support-the following allow attributes are required:

<iframe 
  src="https://configurator.orbital.vision/[API_KEY]/[PRODUCT_OR_RANGE_PATH]"
  allow="
    accelerometer;
    autoplay;
    clipboard-write;
    encrypted-media;
    gyroscope;
    picture-in-picture;
    xr-spatial-tracking;
    fullscreen"
  style="width: 100%; height: 100%;"
></iframe>
  • accelerometer: Allows the 3D configurator to access device orientation data, used for augmented reality interactions on mobile.
  • autoplay: Permits automatic playback of any embedded 3D or media assets without explicit user interaction.
  • clipboard-write: Allows copying data to the user's clipboard. This may be used for future feature support (for example, quickly sharing config data).
  • encrypted-media: Enables playback of protected or DRM-encrypted content if the configurator requires secure media streams.
  • gyroscope: Grants access to motion sensors needed for advanced AR or VR features on mobile devices.
  • picture-in-picture: Allows the configurator to enter picture-in-picture mode in future updates.
  • xr-spatial-tracking: Essential for AR or VR sessions under WebXR, enabling correct spatial tracking of the virtual object in a real-world environment.
  • fullscreen: Permits the configurator to be viewed in full screen, improving the immersive 3D or AR/VR experience.

3. Initial Handshake: Messages from the Configurator

When the configurator finishes loading, it will send messages to the parent window detailing its initial state. These messages will then resend any time the data in the previous messages becomes stale.

ALL_PRODUCTS

Description:

A complete list of products the configurator can display at load time, this will have a single product only if you did not use the range path.

Example:

{
  "type": "ALL_PRODUCTS",
  "payload": [
    {
      "dimensionX": 250,
      "dimensionY": 100,
      "dimensionZ": 100,
      "id": 1,
      "manufacturerId": 10,
      "name": "Sofa A",
      "price": 120000,
      "pricing": {
        "Leather": {
          "stne": 2000,
          "tan": 2500,
          "black": 2500,
          // etc...
        },
        "Legs": {
          "oak": 2000,
          "metal": 2500,
          // etc...
        },
        "Products": {
          "Example Sofa": 10000,
          "Example Large Sofa": 15000,
          // etc...
        }
      },
      "productId": 66,
      "retailerAccess": true,
      "retailerId": 9,
      "sku": "example-sofa"
    },
    {
      "dimensionX": 200,
      "dimensionY": 80,
      "dimensionZ": 80,
      "id": 2,
      "manufacturerId": 11,
      "name": "Sofa B",
      "price": 150000,
      "pricing": {
        "Leather": {
          "red": 3000,
          "brown": 1000,
          // etc...
        },
        "Legs": {
          "oak": 1000,
          "metal": 2000,
          // etc...
        },
        "Products": {
          "Example Sofa B": 7000,
          "Example Large Sofa B": 9000,
          // etc...
        }
      },
      "productId": 67,
      "retailerAccess": true,
      "retailerId": 9,
      "sku": "example-sofa-b"
    }
  ]
}
  • price and pricing are always in the lowest form of that currency, for example in pence (e.g., 150000 = £1,500.00).

RANGE

Description:

Information about the range currently in use (if you're loading a range-based URL).

Example:

{
  "type": "RANGE",
  "payload": {
    "id": 99,
    "name": "Paris Collection",
    "sku": "paris-collection"
  }
}
  • id matches the [RANGE_ID] passed in the URL.

CURRENT_PRODUCT_ID

Description:

Tells you which specific product is currently loaded by the configurator.

Example:

{
  "type": "CURRENT_PRODUCT_ID",
  "payload": 1
}
  • payload is a simple number in this case (the product ID).

CONFIGURATOR_STATE

Description:

The complete structure of the configurator, detailing available options, groups, selections, and the user's current selections.

Quite often the group name will be 'Default Group' or 'Default' if there is only one group, so when designing your UI we recommend hiding groups and just showing the selections directly nested within their option when there exists only a single group.

Example:

{
  "type": "CONFIGURATOR_STATE",
  "payload": {
    "retailerId": 9,
    "screenshotThumbnail": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/screenshot/productId/100_uuid.webp",
    "configuratorSettings": {
      "availableProductFilters": {
        "Legs": ["Wood Type", "Leg Size"],
        "Fabric": ["Colour", "Type", "Pattern"],
        "Trims": [],
      },
      "defaultSelections": [
        {
          "optionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
          "groupId": "6ac505da-3270-4b42-98b3-9b54bcae4d36",
          "selectionId": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4"
        },
        {
          "optionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
          "groupId": "2d6eec44-daa1-407d-bb31-ee06a06660ed",
          "selectionId": "84401d85-25eb-4eef-9e7e-7c91679d4cf1"
        }
      ],
      "visibleSelections": [
        {
          "optionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
          "groupId": "6ac505da-3270-4b42-98b3-9b54bcae4d36",
          "selectionId": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4"
        },
        // etc...
      ]
    },
    "options": [
      {
        "id": "4fe89411-d6b9-4a67-bbcc-905eab673770",
        "name": "Legs",
        "isRequired": true,
        "uuid": "4fe89411-d6b9-4a67-bbcc-905eab673770",
        "groups": [
          {
            "configuratorOptionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
            "id": "6ac505da-3270-4b42-98b3-9b54bcae4d36",
            "name": "default",
            "selections": [
              {
                "blurHash": "LkEoj[ofR*WBM{WBt7ayS2ayof",
                "configuratorGroupId": "6ac505da-3270-4b42-98b3-9b54bcae4d36",
                "configuratorOptionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
                "id": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4",
                "filter": {
                  "Wood Type": ["Oak"],
                  "Leg Size": ["Small"]
                },
                "miniThumbnails": {
                  "large": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/250_uuid.webp",
                  "medium": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/150_uuid.webp",
                  "small": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/50_uuid.webp"
                },
                "name": "Oak",
                "sku": "LEGS_OAK",
                "thumbnail": "https://cdn.example.com/images/oak-legs.jpg",
                "uuid": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4"
              },
              {
                "blurHash": "LLHV}tay~qa#?wM{RjozofM{WBRj",
                "configuratorGroupId": "6ac505da-3270-4b42-98b3-9b54bcae4d36",
                "configuratorOptionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
                "id": "84401d85-25eb-4eef-9e7e-7c91679d4cf1",
                "filter": {
                  "Wood Type": ["Metal"],
                  "Leg Size": ["Medium"]
                },
                "miniThumbnails": {
                    "large": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/250_uuid.webp",
                    "medium": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/150_uuid.webp",
                    "small": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/50_uuid.webp"
                },
                "name": "Metal",
                "sku": "LEGS_METAL",
                "thumbnail": "https://cdn.example.com/images/metal-legs.jpg",
                "uuid": "84401d85-25eb-4eef-9e7e-7c91679d4cf1"
              }
            ]
          }
        ]
      },
      {
        "id": "172b84ab-2834-41ec-88b4-0ae3ef2776c4",
        "name": "Fabric",
        "isRequired": true,
        "uuid": "172b84ab-2834-41ec-88b4-0ae3ef2776c4",
        "groups": [
          {
            "configuratorOptionId": "172b84ab-2834-41ec-88b4-0ae3ef2776c4",
            "id": "2d6eec44-daa1-407d-bb31-ee06a06660ed",
            "name": "Velvet",
            "selections": [
              {
                "blurHash": "LkEoj[ofR*WBM{WBt7ayS2ayof",
                "configuratorGroupId": "2d6eec44-daa1-407d-bb31-ee06a06660ed",
                "configuratorOptionId": "172b84ab-2834-41ec-88b4-0ae3ef2776c4",
                "id": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4",
                "filter": {
                  "Fabric Type": ["Velvet"],
                },
                "miniThumbnails": {
                    "small": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/50_uuid.webp",
                    "medium": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/150_uuid.webp",
                    "large": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/250_uuid.webp"
                },
                "name": "Blue Fabric",
                "sku": "FAB_BLUE",
                "thumbnail": "https://cdn.example.com/images/fabric-blue.jpg",
                "uuid": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4"
              },
              {
                "blurHash": "LQI6j@ayxuxu%MD*i^RkxuRkRjWB",
                "configuratorGroupId": "2d6eec44-daa1-407d-bb31-ee06a06660ed",
                "configuratorOptionId": "172b84ab-2834-41ec-88b4-0ae3ef2776c4",
                "id": "84401d85-25eb-4eef-9e7e-7c91679d4cf1",
                "filter": {
                  "Fabric Type": ["Plush"],
                },
                "miniThumbnails": {
                    "small": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/50_uuid.webp",
                    "medium": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/150_uuid.webp",
                    "large": "https://bucket-name.s3.region.amazonaws.com/configurator/thumbnails/miniThumbs/productId/250_uuid.webp"
                },
                "name": "Red Fabric",
                "sku": "FAB_RED",
                "thumbnail": "https://cdn.example.com/images/fabric-red.jpg",
                "uuid": "84401d85-25eb-4eef-9e7e-7c91679d4cf1"
              }
            ]
          }
        ]
      }
    ],
    "selectedSelections": [
      {
        "optionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
        "groupId": "6ac505da-3270-4b42-98b3-9b54bcae4d36",
        "selectionId": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4"
      },
      {
        "optionId": "172b84ab-2834-41ec-88b4-0ae3ef2776c4",
        "groupId": "2d6eec44-daa1-407d-bb31-ee06a06660ed",
        "selectionId": "84401d85-25eb-4eef-9e7e-7c91679d4cf1"
      }
    ]
  }
}
  • Each selection comes with a thumbnail, and mini thumbnails. The mini thumnails are sized 50px, 150px and 250px (small, medium large)
  • They are all webp which as you can see from https://caniuse.com/webp is supported in nearly all browsers. In 2025 it is completely safe to use and we recommend that you use these performant mini thumbnails. If you do not want to use them, there is also the original thumbnail jpg for you to use with an image service/cdn.
  • Each option can contain one or more groups. Each group can contain one or more selections.
  • selectedSelections tells you exactly which selectionId the user has chosen for each option/group.

CURRENT_PRICE

Description:

The calculated price based on the user's current selections.

Example:

{
  "type": "CURRENT_PRICE",
  "payload": {
    "formattedPrice": "£1,036.00",
    "totalPrice": 103600,
    "subtotal": 129500,
    "formattedSubtotal": "£1,295.00",
    "discount": {
      "amount": 25900,
      "formattedAmount": "£259.00",
      "percentage": 20
    },
    "priceBreakdown": [
      {
        "category": "Ranges",
        "formattedPrice": "£0.00",
        "name": "Paris",
        "price": 0,
        "sku": "paris-range"
      },
      {
        "category": "Products",
        "formattedPrice": "£1,200.00",
        "name": "Example Sofa",
        "price": 120000,
        "sku": "example-sofa"
      },
      {
        "category": "Leather",
        "formattedPrice": "£300.00",
        "name": "Red",
        "price": 30000,
        "sku": "LEATHER_RED"
      },
    ]
  }
}
  • formattedPrice is a user-facing string (localized).
  • totalPrice is an integer in the lowest form of that currency (103600 = £1,036.00).
  • subtotal is the price before any discounts are applied (129500 = £1,295.00).
  • formattedSubtotal is the formatted string representation of the subtotal.
  • discount contains the discount information with amount (in lowest currency form), formattedAmount (localized string), and percentage (discount percentage).
  • priceBreakdown is a breakdown of the total price, including the price of the range, product, and any selections such as fabric or legs. This will sum to subtotal.
  • Each priceBreakdown row also contains subtotal, formattedSubtotal, discountedAmount, formattedDiscountAmount and discountPercentage for that line.

Multi-product modular (snap2) CURRENT_PRICE

For multi-product modular configurators, the iframe replaces priceBreakdown with productBreakdowns - one entry per product type in the scene, each with its own quantity, selections and per-line discount fields.

{
  "type": "CURRENT_PRICE",
  "payload": {
    "formattedPrice": "£3,200.00",
    "totalPrice": 320000,
    "subtotal": 320000,
    "formattedSubtotal": "£3,200.00",
    "discount": {
      "amount": 0,
      "formattedAmount": "£0.00",
      "percentage": 0
    },
    "productBreakdowns": [
      {
        "productId": "66",
        "modelId": "12",
        "name": "Grand Sofa",
        "quantity": 2,
        "subtotal": 120000,
        "formattedSubtotal": "£1,200.00",
        "price": 240000,
        "formattedPrice": "£2,400.00",
        "discountedAmount": 0,
        "formattedDiscountAmount": "£0.00",
        "discountPercentage": 0,
        "selections": [
          {
            "name": "Oak",
            "price": 2000,
            "formattedPrice": "£20.00",
            "thumbnail": "https://cdn.example.com/images/oak-legs.jpg"
          }
        ],
        "image": "https://cdn.example.com/images/grand-sofa.jpg"
      },
      {
        "productId": "67",
        "modelId": "14",
        "name": "Corner Module",
        "quantity": 1,
        "subtotal": 80000,
        "formattedSubtotal": "£800.00",
        "price": 80000,
        "formattedPrice": "£800.00",
        "discountedAmount": 0,
        "formattedDiscountAmount": "£0.00",
        "discountPercentage": 0,
        "selections": [],
        "image": "https://cdn.example.com/images/corner-module.jpg"
      }
    ]
  }
}
  • productId matches the corresponding key in the multi-product CURRENT_SKU payload, and modelId matches the underlying 3D model.
  • price is per-line total (already multiplied by quantity); subtotal is the per-unit price before quantity.
  • selections lists the per-product cost contribution of each currently selected option (e.g. fabric, legs).
  • If you are integrating via the ov25-ui package, both priceBreakdown and productBreakdowns are normalized into a unified lines[] array with mode: 'single' | 'multi'. See the UI Package Integration guide.

CURRENT_SKU

Description:

The SKU representing the current product configuration. Helpful for backend or e-commerce tracking.

The shape differs between standard single-product configurators and multi-product modular (snap2) configurators.

Single-product example:

{
  "type": "CURRENT_SKU",
  "payload": {
    "skuString": "WINDRUSH/GRAND/LEGS_OAK/FAB_RED",
    "skuMap": {
	    "Range": "WINDRUSH",
	    "Product": "GRAND",
      "Legs": "LEGS_OAK",
      "Fabric": "FAB_RED"
    }
  }
}

Multi-product modular (snap2) example:

The payload is an object keyed by productId. Each entry is a billable line in the scene (one entry per product type, with its own quantity).

{
  "type": "CURRENT_SKU",
  "payload": {
    "66": {
      "skuString": "GRAND/LEGS_OAK/FAB_RED",
      "skuMap": {
        "Products": "GRAND",
        "Legs": "LEGS_OAK",
        "Fabric": "FAB_RED"
      },
      "quantity": 2
    },
    "67": {
      "skuString": "CORNER/LEGS_OAK/FAB_RED",
      "skuMap": {
        "Products": "CORNER",
        "Legs": "LEGS_OAK",
        "Fabric": "FAB_RED"
      },
      "quantity": 1
    }
  }
}
  • skuString is a / separated combination of the product SKU and each selection SKU.
  • skuMap shows how each option name maps to the underlying SKU.
  • For multi-product modular configurators, iterate Object.entries(payload) - each value has its own skuString, skuMap and quantity.
  • If you are integrating via the ov25-ui package, both shapes are normalized into a single OnChangePayload with mode: 'single' | 'multi' and lines[]. See the UI Package Integration guide.

SELECTED_SELECTIONS

Description:

The full set of currently selected { optionId, groupId, selectionId } triples. Sent any time the selection set changes (in addition to CONFIGURATOR_STATE). Useful if you want a lightweight signal of selection changes without re-parsing the entire configurator state.

Example:

{
  "type": "SELECTED_SELECTIONS",
  "payload": [
    {
      "optionId": "4fe89411-d6b9-4a67-bbcc-905eab673770",
      "groupId":  "6ac505da-3270-4b42-98b3-9b54bcae4d36",
      "selectionId": "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4"
    }
  ]
}

Description:

A direct link to an AR experience, used when the current device is not appropriate.

Example:

{
  "type": "AR_PREVIEW_LINK",
  "payload": "https://configurator.orbital.vision/ar-preview/{key}"
}
  • It is recommended that you display this link with a QR code to scan from a mobile device.
  • On iOS devices, this will open AR Quick Look. Unfortunately apple refuses to support other methods such as webXR which are otherwise universally supported. Additionally, USDZ does not have full support for lighting/custom shaders and there is also a hard limit on textures that we can use in the material. This leads to a huge loss of quality when using AR on any IOS device, such as an iPhone. We are actively monitoring changes from apple and if it becomes possible in the future, we will address this.

AVAILABLE_CAMERAS

Description:

The list of camera presets the configurator exposes for the current product (if any). Sent whenever the camera list changes.

Example:

{
  "type": "AVAILABLE_CAMERAS",
  "payload": [
    { "id": "front", "displayName": "Front" },
    { "id": "side",  "displayName": "Side"  },
    { "id": "top",   "displayName": "Top"   }
  ]
}

Send SELECT_CAMERA (see section 7) to switch to a given camera by id.


AVAILABLE_LIGHTS

Description:

The list of light groups (lighting presets) available for the current scene. Sent whenever the light group list changes.

Example:

{
  "type": "AVAILABLE_LIGHTS",
  "payload": [
    { "id": "studio",  "displayName": "Studio"  },
    { "id": "warm",    "displayName": "Warm"    },
    { "id": "outdoor", "displayName": "Outdoor" }
  ]
}

Send SELECT_LIGHT (see section 7) to switch to a given light group by id.


AVAILABLE_FABRICS

Description:

For fabric-account integrations, the list of fabrics that can be applied as a material override on the current product. Only sent when fabric overrides are available.

Example (truncated):

{
  "type": "AVAILABLE_FABRICS",
  "payload": [
    {
      "id": 123,
      "name": "Velvet Red",
      "sku": "VEL_RED",
      "thumbnail": "https://cdn.example.com/images/velvet-red.jpg"
    }
  ]
}

Send SET_FABRIC_OVERRIDE (see section 7) to apply a fabric to a specific material.


COMPATIBLE_MODULES (multi-product modular only)

Description:

For multi-product modular (snap2) configurators, the list of modules that can be added or substituted at the user's current selection point. Sent on initial load and any time the selected attachment point or selected object changes.

Example:

{
  "type": "COMPATIBLE_MODULES",
  "payload": {
    "isInitialLoad": true,
    "modules": [
      {
        "productId": 66,
        "product": { "id": 66, "name": "Grand Sofa", "imageUrl": "https://...", "hasImage": true },
        "model":   { "modelPath": "https://.../grand.glb", "modelId": 12 },
        "dimensions": { "x": 250, "y": 100, "z": 100 }
      }
    ]
  }
}
  • When isInitialLoad === true, the array contains every available module for the initial pick (no scene yet).
  • After the first module is placed, the array will only contain modules compatible with the currently selected attachment point or object, or be empty when nothing is selected.

Send SELECT_MODULE (see section 7) to add or replace a module.


SELECT_MODULE_RECEIVED (multi-product modular only)

Acknowledgement of a SELECT_MODULE command. Payload: { "success": boolean, "modelPath": string, "modelId": string }.


SCREENSHOT_URL

Description:

Response to CAPTURE_SCREENSHOT (see section 7). Contains the uploaded URL of the captured PNG screenshot of the configurator viewport.

{
  "type": "SCREENSHOT_URL",
  "payload": {
    "url": "https://bucket-name.s3.region.amazonaws.com/configurator/screenshots/{key}.png",
    "cdnUrl": "https://cdn.example.com/configurator/screenshots/{key}.png"
  }
}

If the capture fails, an ERROR message is sent instead (see below).


CAMERA_RECENTERED

Description:

Acknowledgement of a RECENTER_CAMERA command. Payload: { "success": true }.


SNAP2_SAVE_RESPONSE (multi-product modular only)

Description:

Response to REQUEST_SNAP2_SAVE. Returns the saved configuration UUID that can later be passed back as configurationUuid to restore the scene.

{
  "type": "SNAP2_SAVE_RESPONSE",
  "payload": {
    "success": true,
    "uuid": "68245136-580c-4481-864c-1da82f3a50db"
  }
}

On failure: { "success": false, "error": "<message>" }.


CURRENT_BED_SIZE (bed configurator only)

Description:

For bed configurators, the currently selected bed size. Sent whenever the bed size changes.

{ "type": "CURRENT_BED_SIZE", "payload": { "size": "king" } }

ANIMATION_STATE

Description:

Notifies the parent of the current animation state for the loaded model. Sent on every state change.

Possible string values: "unavailable", "stop", "loop", "open", "close".

{ "type": "ANIMATION_STATE", "payload": "loop" }

SHOW_OPTION / HIDE_OPTION / SHOW_GROUP / HIDE_GROUP / SHOW_SELECTION / HIDE_SELECTION

Description:

Diff events emitted whenever the visible UI structure changes (for example after a rule hides a group, or when fabric overrides bring a previously-hidden option back). Useful if your parent UI is mirroring the configurator's option/group/selection tree and you want to track visibility without re-diffing CONFIGURATOR_STATE yourself.

{ "type": "SHOW_OPTION",    "payload": { "optionId": "..." } }
{ "type": "HIDE_OPTION",    "payload": { "optionId": "..." } }
{ "type": "SHOW_GROUP",     "payload": { "optionId": "...", "groupId": "..." } }
{ "type": "HIDE_GROUP",     "payload": { "optionId": "...", "groupId": "..." } }
{ "type": "SHOW_SELECTION", "payload": { "optionId": "...", "groupId": "...", "selectionId": "..." } }
{ "type": "HIDE_SELECTION", "payload": { "optionId": "...", "groupId": "...", "selectionId": "..." } }

TRANSITION_SNAPSHOT / TRANSITION_SNAPSHOT_ERROR

Description:

Response to a REQUEST_TRANSITION_SNAPSHOT command (see section 7). Used to capture a low-latency ImageBitmap of the canvas for transition animations between products.

TRANSITION_SNAPSHOT is not a JSON payload - the ImageBitmap is sent as a transferable on the message itself:

window.addEventListener("message", (event) => {
  if (event.data?.type === "TRANSITION_SNAPSHOT") {
    const { requestId, bitmap } = event.data; // bitmap is an ImageBitmap
    // draw onto a canvas, then bitmap.close() when done
  }
});

On failure: { type: "TRANSITION_SNAPSHOT_ERROR", requestId, payload: '{"message":"..."}' }.


ERROR

Description:

Generic error response. Sent when an inbound command (e.g. CAPTURE_SCREENSHOT, SELECT_CAMERA, SELECT_LIGHT, SELECT_MODULE) fails or receives an invalid payload.

{ "type": "ERROR", "payload": { "message": "Camera not found" } }

IS_LOADING

Description:

Indicates whether the configurator is currently loading a new product or model. Useful for showing a loading overlay in the parent UI.

{ "type": "IS_LOADING", "payload": true }

How to Use These Messages

  • All messages are posted to the parent window. You can intercept them with:

    window.addEventListener("message", (event) => {
      const { type, payload } = event.data;
      // Parse and handle each type accordingly
    });
  • Parse payload with JSON.parse(...) if the configurator sends it as a string.

  • Update your UI or application state with product data, pricing, SKU, or AR links as needed.


4. Listening for Messages (Parent Application)

In your parent application, add an event listener for message:

 
window.addEventListener("message", (event) => {
  const { type, payload } = event.data;
  if (!type) return;
 
  // Safely parse payload
  let data;
  try {
    data = JSON.parse(payload);
  } catch (error) {
    console.error("Failed to parse configurator message payload:", payload);
    return;
  }
 
  switch (type) {
    case "ALL_PRODUCTS":
      // e.g. store in your state
      console.log("All products:", data);
      break;
    case "CURRENT_PRICE":
      console.log("Updated price:", data);
      break;
    case "CURRENT_PRODUCT_ID":
      console.log("Active product ID:", data);
      break;
    // ... handle other message types
  }
});

5. Sending Messages to the Configurator

You can control the configurator by sending messages from the parent app to the iframe's contentWindow. For example:

const iframeEl = document.getElementById("configurator-iframe");
 
function sendToConfigurator(type, payload) {
  iframeEl.contentWindow.postMessage(
    { type, payload: JSON.stringify(payload) },
    "*"
  );
}

Below are the most common commands:

  1. SELECT_PRODUCT

    Instructs the configurator to switch to another product:

    sendToConfigurator("SELECT_PRODUCT", 2);
  2. SELECT_SELECTION

    Applies a user's choice (e.g., new fabric or color) for a specific option. The iframe accepts two payload shapes on the same message type:

    A. IDs (stable, recommended when you already have UUIDs from CONFIGURATOR_STATE or SELECTED_SELECTIONS):

    sendToConfigurator("SELECT_SELECTION", {
      optionId: "4fe89411-d6b9-4a67-bbcc-905eab673770",
      groupId: "6ac505da-3270-4b42-98b3-9b54bcae4d36",
      selectionId: "0fa4b7e2-caf5-4d6b-9400-3c612427eaf4"
    });

    If the payload object includes a selectionId property, it is treated as this legacy shape.

    B. Single display-name pair (fuzzy-matched against the live menu):

    Send exactly one string key and one string value: the option display name as the key, and the selection display name (or a close variant) as the value. Useful when your storefront encodes choices as human-readable or slug-like strings instead of configurator UUIDs.

    sendToConfigurator("SELECT_SELECTION", {
      Legs: "Medium Oak"
    });
    // or e.g. uppercase / slug-style labels from another system:
    sendToConfigurator("SELECT_SELECTION", {
      LEGS: "FOOT_COLOUR__MEDIUM_OAK"
    });

    The iframe resolves this to { optionId, groupId, selectionId } internally, then applies the selection the same way as shape A. If the payload cannot be parsed, has more than one key–value pair, or no confident match is found, the iframe posts an ERROR message (see section 7. Parent → Configurator Message Types (Detailed) below).

    How name-based matching works

    Matching runs twice: first to pick a configurator option by the object key, then to pick a selection under that option by the object value. For each name string the algorithm:

    1. Normalize — lowercase; replace common separators (_, -, /, punctuation) with spaces; strip non-alphanumeric characters; collapse whitespace. This turns values like FOOT_COLOUR__MEDIUM_OAK into comparable tokens (foot, colour, medium, oak).
    2. Exact match — if the normalized string equals a candidate's normalized name, that candidate wins immediately.
    3. Token-set recall — otherwise, score each candidate by how many of its distinct tokens appear in the query (intersection ÷ number of distinct candidate tokens). This favours selections whose words are all present in the query even when the query has extra words (e.g. query tokens include foot and colour, candidate is Medium Oak → high score). If the best score is at least 0.5, that candidate is accepted. Ties are broken by Levenshtein distance on the sorted space-joined token strings, then by Levenshtein on the full normalized strings.
    4. Whole-string Levenshtein fallback — if the token score is below 0.5, the best candidate is still accepted when its normalized full-string edit distance to the query is within a length-scaled cap: min(24, max(3, ceil(queryCharacterLength × 0.22))). This catches near-misses such as singular vs plural (Leg vs Legs).

    Caveats: If two selections under the same option share the same display name (or fuzzy-tie), the first matching row in menu order may win. Prefer shape A when you need deterministic behaviour.

  3. VIEW_DIMENSIONS

    Shows or hides dimension lines on the 3D model. Optionally pass styles to override the iframe overlay CSS that the dimension annotations live inside (useful when the host page styles differ from the iframe's defaults):

    sendToConfigurator("VIEW_DIMENSIONS", { dimensions: true });
    // or
    sendToConfigurator("VIEW_DIMENSIONS", { dimensions: false });
    // optional styling overrides (snap2 + bed configurators)
    sendToConfigurator("VIEW_DIMENSIONS", {
      dimensions: true,
      styles: { color: "#fff", background: "rgba(0,0,0,0.6)" }
    });
  4. VIEW_MINI_DIMENSIONS (multi-product modular only)

    Toggles the smaller per-module dimension overlays in snap2 scenes. Same payload shape as VIEW_DIMENSIONS.

    sendToConfigurator("VIEW_MINI_DIMENSIONS", { dimensions: true });
  5. ENTER_AR

    Attempts to start an augmented reality session or, if necessary, returns an AR_PREVIEW_LINK that you can display. You may pass userAgentType ("ios" | "android" | undefined) to force which AR pipeline to attempt:

    sendToConfigurator("ENTER_AR", {});
    sendToConfigurator("ENTER_AR", { userAgentType: "android" });
  6. ENTER_VR

    (If supported) instructs the configurator to begin a VR session:

    sendToConfigurator("ENTER_VR", {});
  7. TOGGLE_ANIMATION

    Cycles through animation states for the loaded model (stoploop, openclose). The new state is broadcast back via ANIMATION_STATE.

    sendToConfigurator("TOGGLE_ANIMATION", {});
  8. CAPTURE_SCREENSHOT

    Captures a PNG of the current viewport, uploads it, and replies with a SCREENSHOT_URL message (or an ERROR on failure).

    sendToConfigurator("CAPTURE_SCREENSHOT", {});
  9. SELECT_CAMERA

    Switches to one of the cameras advertised in AVAILABLE_CAMERAS. Payload is the camera id as a JSON value.

    sendToConfigurator("SELECT_CAMERA", "front");
  10. RECENTER_CAMERA

    Recenters the active camera on the current model. Replies with CAMERA_RECENTERED.

    sendToConfigurator("RECENTER_CAMERA", {});
  11. SELECT_LIGHT

    Switches to one of the light groups advertised in AVAILABLE_LIGHTS. Payload is the light group id as a JSON value.

    sendToConfigurator("SELECT_LIGHT", "studio");
  12. SET_FABRIC_OVERRIDE (fabric accounts only)

    Applies a fabric texture (from AVAILABLE_FABRICS) to a specific named material on the current product.

    sendToConfigurator("SET_FABRIC_OVERRIDE", {
      materialName: "MainFabric",
      textureId: 123
    });
  13. SELECT_MODULE (multi-product modular only)

    Adds a new module to the scene (when nothing is placed yet) or replaces the currently selected module. Use the modelPath and modelId from a COMPATIBLE_MODULES entry.

    sendToConfigurator("SELECT_MODULE", {
      modelPath: "https://.../grand.glb",
      modelId: 12
    });
  14. CLOSE_MODULE_SELECT_MENU (multi-product modular only)

    Clears the currently selected attachment point and selected object, closing any module-picker UI driven from COMPATIBLE_MODULES.

    sendToConfigurator("CLOSE_MODULE_SELECT_MENU", {});
  15. REQUEST_SNAP2_SAVE (multi-product modular only)

    Persists the current snap2 scene server-side and replies with a SNAP2_SAVE_RESPONSE containing the uuid. Pass that uuid back as configurationUuid on a future load to restore the scene.

    sendToConfigurator("REQUEST_SNAP2_SAVE", {});
  16. TOGGLE_HIDE_ALL (multi-product modular only)

    Toggles a "hide everything" mode that visually hides all placed modules in a snap2 scene. Useful for clean screenshots or comparing layouts.

    sendToConfigurator("TOGGLE_HIDE_ALL", {});
  17. REQUEST_TRANSITION_SNAPSHOT

    Asks the configurator to capture a low-latency ImageBitmap of the canvas for use in a transition animation (e.g. when crossfading between products). Replies with a TRANSITION_SNAPSHOT message (or TRANSITION_SNAPSHOT_ERROR) carrying the same requestId.

    sendToConfigurator("REQUEST_TRANSITION_SNAPSHOT", { requestId: "req-1" });

6. Configurator → Parent Message Types (Detailed)

Below is a reference table for all inbound messages the configurator sends and their payload content:

TypeMeaningPayload (JSON)
ALL_PRODUCTSFull list of available products.Array of product objects, each with dimensionX, dimensionY, dimensionZ, id, manufacturerId, name, price, pricing, productId, retailerAccess, retailerId, sku and other data.
RANGEData about the current range or collection.An object with id, name, sku and possibly other range-specific fields.
CURRENT_PRODUCT_IDIndicates which product is actively displayed.A single number (e.g., 45).
CONFIGURATOR_STATEThe complete nested structure of options, groups, selections, etc.An object that includes retailerId, screenshotThumbnail, configuratorSettings (object), options (array), selectedSelections (array), and (in snap2 scenes) snap2Objects and fabricReplaceableOptionIds / optionIdToMaterialName.
SELECTED_SELECTIONSLightweight diff of currently selected { optionId, groupId, selectionId } triples.Array of selection triples.
CURRENT_PRICEThe order's total price and breakdown.Object with formattedPrice, totalPrice, subtotal, formattedSubtotal, discount, and either priceBreakdown (single product) or productBreakdowns (multi-product modular).
CURRENT_SKUThe SKU representing the user's current configuration.Single product: object with skuString and skuMap. Multi-product modular: object keyed by productId, each value { skuString, skuMap, quantity }.
AR_PREVIEW_LINKA direct link to an AR preview for compatible devices.A string containing the URL.
SELECT_PRODUCT_RECEIVEDConfirms a SELECT_PRODUCT command was received and processed.Typically the same ID you sent (useful for clearing loading states).
ANIMATION_STATENotifies the parent of the current animation state.One of "unavailable", "stop", "loop", "open", "close".
AVAILABLE_CAMERASThe available named camera presets.Array of { id, displayName }.
CAMERA_RECENTEREDAcknowledgement of RECENTER_CAMERA.{ success: true }.
AVAILABLE_LIGHTSThe available named light groups.Array of { id, displayName }.
AVAILABLE_FABRICSFabrics available as material overrides (fabric accounts).Array of fabric objects (id, name, sku, thumbnail, etc).
COMPATIBLE_MODULES(snap2) Modules that can be added at the current selection point.{ modules: ProductOrientation[], isInitialLoad: boolean }.
SELECT_MODULE_RECEIVED(snap2) Acknowledgement of SELECT_MODULE.{ success: boolean, modelPath: string, modelId: number }.
SCREENSHOT_URLResponse to CAPTURE_SCREENSHOT.{ url: string, cdnUrl?: string }.
SNAP2_SAVE_RESPONSE(snap2) Response to REQUEST_SNAP2_SAVE.{ success: boolean, uuid?: string, error?: string }.
CURRENT_BED_SIZE(bed configurator) Current selected bed size.{ size: string | null }.
SHOW_OPTION / HIDE_OPTIONUI visibility diff for an option.{ optionId: string }.
SHOW_GROUP / HIDE_GROUPUI visibility diff for a group.{ optionId, groupId }.
SHOW_SELECTION / HIDE_SELECTIONUI visibility diff for a selection.{ optionId, groupId, selectionId }.
TRANSITION_SNAPSHOTResponse to REQUEST_TRANSITION_SNAPSHOT.Not JSON. Carries { requestId, bitmap: ImageBitmap } as a transferable.
TRANSITION_SNAPSHOT_ERRORFailure response to REQUEST_TRANSITION_SNAPSHOT.{ requestId, payload: '{"message":"..."}' }.
IS_LOADINGIndicates the configurator is loading.A boolean.
ERRORGeneric error response to a failed inbound command.{ message: string } and optional context fields (e.g. name-based SELECT_SELECTION failures may include optionName and selectionName).

7. Parent → Configurator Message Types (Detailed)

Below is a reference table for outbound messages you can send to the configurator:

TypeDescriptionExpected Payload
SELECT_PRODUCTLoads a specific product by its ID.A single integer representing the product ID.
SELECT_SELECTIONUpdates the user's choice for a specific option.Either { optionId, groupId, selectionId } (when selectionId is present), or a single { "<OptionDisplayName>": "<SelectionDisplayNameOrSlug>" } pair for fuzzy resolution (see §5 item 2).
VIEW_DIMENSIONSShows or hides product dimension lines.{ dimensions: boolean, styles?: object }.
VIEW_MINI_DIMENSIONS(snap2) Toggles per-module dimension overlays.{ dimensions: boolean, styles?: object }.
ENTER_ARInitiates augmented reality mode, if supported.{} or { userAgentType: "ios" | "android" }.
ENTER_VRTries to start a virtual reality session, if supported.{}.
TOGGLE_ANIMATIONCycles through animation states (stoploop, openclose).{}. Affects all animation mixers in the scene.
CAPTURE_SCREENSHOTCaptures and uploads a PNG of the current viewport.{}. Replies with SCREENSHOT_URL or ERROR.
SELECT_CAMERASwitches to a camera from AVAILABLE_CAMERAS.A string camera id (JSON).
RECENTER_CAMERARecenters the active camera on the model.{}. Replies with CAMERA_RECENTERED.
SELECT_LIGHTSwitches to a light group from AVAILABLE_LIGHTS.A string light group id (JSON).
SET_FABRIC_OVERRIDE(fabric accounts) Apply a fabric to a named material.{ materialName: string, textureId: number }.
SELECT_MODULE(snap2) Add or replace a module at the current selection point.{ modelPath: string, modelId: number }.
CLOSE_MODULE_SELECT_MENU(snap2) Clear selection and close the module picker.{}.
REQUEST_SNAP2_SAVE(snap2) Persist the current scene and reply with its UUID.{}. Replies with SNAP2_SAVE_RESPONSE.
TOGGLE_HIDE_ALL(snap2) Toggle a "hide everything" rendering mode.{}.
REQUEST_TRANSITION_SNAPSHOTCapture an ImageBitmap of the canvas for transition animations.{ requestId: string }. Replies with TRANSITION_SNAPSHOT or TRANSITION_SNAPSHOT_ERROR.

8. Typical Integration Flow

  1. Embed the iFrame
    • Provide the correct URL (with your API key + product or range).
    • Include the required allow attributes for AR/VR, fullscreen, etc.
  2. Listen for the Handshake
    • As soon as the iframe loads, it sends ALL_PRODUCTS, CURRENT_PRODUCT_ID, CONFIGURATOR_STATE, etc.
    • Parse and store these details (e.g., in your parent app's state).
  3. User Interaction in the Parent
    • When a user selects a different product from your UI:

      sendToConfigurator("SELECT_PRODUCT", newProductId);
    • The configurator reloads to that product and returns updated messages (price, SKU, etc.).

  4. Configurator Updates
    • On any selection change, the configurator posts back CURRENT_PRICE, CURRENT_SKU, etc.
    • Update your parent UI or e-commerce logic accordingly.
  5. Special Actions
    • AR: ENTER_AR leads to a WebXR session on Android or an AR_PREVIEW_LINK for iOS.
    • Dimensions: Toggle rendering of dimension helpers on or off via VIEW_DIMENSIONS.

9. Example Minimal Code

Below is an extremely simplified example (plain HTML + JS) showing a typical integration:

<!DOCTYPE html>
<html>
  <head>
    <title>3D Configurator Integration</title>
    <style>
      #configurator-frame {
        width: 800px;
        height: 600px;
        border: 1px solid #ccc;
      }
    </style>
  </head>
  <body>
    <h1>Configurator Integration Demo</h1>
 
    <!-- Replace 'abc123' and '45' with your API key and product/range details -->
    <iframe id="configurator-frame"
      src="https://configurator.orbital.vision/abc123/45"
      allow="
        accelerometer;
        autoplay;
        clipboard-write;
        encrypted-media;
        gyroscope;
        picture-in-picture;
        xr-spatial-tracking;
        fullscreen
      "
    ></iframe>
 
    <div style="margin-top: 20px;">
      <button id="selectProductBtn">Select Product #2</button>
      <button id="toggleDimensionsBtn">Toggle Dimensions</button>
      <button id="enterARBtn">Enter AR</button>
    </div>
 
    <script>
      const iframeEl = document.getElementById("configurator-frame");
      let showDimensions = false;
 
      function sendToConfigurator(type, payload) {
        iframeEl.contentWindow.postMessage(
          { type, payload: JSON.stringify(payload) },
          "*"
        );
      }
 
      window.addEventListener("message", (event) => {
        const { type, payload } = event.data;
        if (!type) return;
 
        let data;
        try {
          data = JSON.parse(payload);
        } catch (e) {
          console.error("Error parsing payload:", payload);
          return;
        }
 
        switch (type) {
          case "ALL_PRODUCTS":
            console.log("All products:", data);
            break;
          case "CURRENT_PRICE":
            console.log("Current price:", data);
            break;
          case "CURRENT_PRODUCT_ID":
            console.log("Active product ID:", data);
            break;
          case "AR_PREVIEW_LINK":
            console.log("AR link:", data);
            // Possibly show a QR code for desktop users
            break;
          // ... other message handlers ...
        }
      });
 
      document.getElementById("selectProductBtn").addEventListener("click", () => {
        sendToConfigurator("SELECT_PRODUCT", 2);
      });
 
      document.getElementById("toggleDimensionsBtn").addEventListener("click", () => {
        showDimensions = !showDimensions;
        sendToConfigurator("VIEW_DIMENSIONS", { dimensions: showDimensions });
      });
 
      document.getElementById("enterARBtn").addEventListener("click", () => {
        sendToConfigurator("ENTER_AR", {});
      });
    </script>
  </body>
</html>

10. Tips & Troubleshooting

  • Cross-Origin: Ensure you have the correct CORS or target origins set.
  • Performance: Since real-time 3D rendering can be resource-intensive, consider deferring the iframe load until it's needed, or show a placeholder until loading completes. A good idea can be showing an image of the product, then when the user selects a configuration item switching to the iframe. This way the iframe will have already loaded in the second or two it takes them to make a selection, hiding the loading state.
  • AR/VR Compatibility: The ENTER_AR and ENTER_VR features rely on the browser's WebXR implementation. Unsupported devices may fall back to returning an AR_PREVIEW_LINK.
  • Event Parsing: Always wrap JSON.parse(payload) in a try/catch block to handle any unexpected responses gracefully.
  • Product Switches: After sending SELECT_PRODUCT, wait until you receive an update before updating the UI, as our system debounces updates.