---
title: "Integration guidelines | Customer&#x27;s Canvas Hub"
canonical: "https://customerscanvas.com/docs/hub/dev-center/frontend-integration/workflow-elements/headless-editor/integration-guidelines/"
---

# Integration guidelines

> Embed the Headless Editor in a page, initialize it, respond to lifecycle events, and finish a customization session by collecting a cart line item. The guidelines cover all initialization scenarios that the editor supports, including restore flows.

## Embed the editor

The Headless Editor is a web component distributed as a bundle managed on Aurigma's CDN. The editor has no UI of its own except the design viewer, so the element that hosts it stays mostly invisible.

1. Add the editor tag to the page.

   ```html
   <au-headless-editor></au-headless-editor>
   ```

   Place the element in the container where you want to show the design viewer, for example next to the product image.

2. Load the script and stylesheet for the Customer's Canvas environment that matches your tenant.

   ```typescript
   const environment = "us"; // "us", "eu", or "au"
   const baseUrl = `https://staticjs-aurigma.azureedge.net/libs/${environment}/workflow-elements/headless-editor`;

   const stylesheet = document.createElement("link");
   stylesheet.rel = "stylesheet";
   stylesheet.href = `${baseUrl}/styles.css`;
   document.head.appendChild(stylesheet);

   const script = document.createElement("script");
   script.src = `${baseUrl}/index.js`;
   script.defer = true;
   document.head.appendChild(script);
   ```

3. Wait for the editor API and get the editor instance.

   ```typescript
   import type { IHeadlessEditor } from "@aurigma/workflow-elements/headless-editor";

   // The editor bundle bootstraps its component onto the existing
   // <au-headless-editor> element. Poll for the editor API on the element.
   function waitForEditorApi(): Promise<void> {
     return new Promise(resolve => {
       const check = (): void => {
         const editor = document.querySelector(
           "au-headless-editor"
         ) as (HTMLElement & IHeadlessEditor) | null;

         if (editor != null && typeof editor.init === "function") {
           resolve();
           return;
         }

         setTimeout(check, 100);
       };

       check();
     });
   }

   await waitForEditorApi();

   const editor = document.querySelector(
     "au-headless-editor"
   ) as (HTMLElement & IHeadlessEditor) | null;
   ```

   The `@aurigma/workflow-elements` package provides the TypeScript declarations for your host application. The web component itself is loaded from the CDN.

## Initialize the editor

Call `init()` with the input, integration, and optional settings. Subscribe to lifecycle events before calling `init()`.

```typescript
import type { IHeadlessEditorInitArgs } from "@aurigma/workflow-elements/headless-editor";

editor.addEventListener("load", () => {
  // The product and its initial design are ready.
});

editor.addEventListener("error", event => {
  console.error("Headless Editor failed to initialize.", (event as CustomEvent<Error>).detail);
});

const configuration: IHeadlessEditorInitArgs = {
  configVersion: 2,
  input: {
    productReferenceId: "<product_reference_id>",
  },
  integration: {
    tenantId: <tenant_id>,
    storefrontId: <storefront_id>,
    cchubUrl: "https://customerscanvashub.com",
    cchubApiGatewayUrl: "https://api.customerscanvashub.com",
    user: {
      id: "<storefront_user_id>",
      token: "<storefront_user_token>",
    },
  },
};

await editor.init(configuration);
```

`init()` starts loading asynchronously and resolves to `true` when the product is loaded. Use the `load` event as the signal that the product and its initial design are ready. The method also accepts an optional `forceReset` argument that forces the editor to reset its state before the initialization.

The `integration` block tells the editor which tenant and storefront to use, and supplies the storefront user credentials. The optional `quantity` property defines the quantity applied to the resulting line item. The `settings` block controls the built-in viewer states; see [Viewer settings and appearance](https://customerscanvas.com/docs/hub/dev-center/frontend-integration/workflow-elements/headless-editor/viewer-settings.md).

### Open a product by product reference

Pass `productReferenceId` to load a product through a product reference that links an external product ID, such as a Shopify product ID, to a Customer's Canvas product. This is the default integration flow for a storefront.

```typescript
await editor.init({
  configVersion: 2,
  input: {
    productReferenceId: "<product_reference_id>",
  },
  integration: { /* ... */ },
});
```

Optionally narrow the returned design variants with `designVariantFilter`, whose fields match the Storefront API product variant designs endpoint.

```typescript
input: {
  productReferenceId: "<product_reference_id>",
  designVariantFilter: {
    sku: "ABC-1234",
  },
},
```

### Open a product by product ID

When your integration already knows the Customer's Canvas PIM product ID, pass `productId`.

```typescript
await editor.init({
  configVersion: 2,
  input: {
    productId: <product_id>,
    productVersionId: <product_version_id>,
  },
  integration: { /* ... */ },
});
```

`productVersionId` is optional. When you omit it, the editor uses the active product version.

### Restore a saved design

To continue an existing customization session, pass the saved design together with the product data. The editor supports two restore mechanisms: restore by PIM product data and restore by product reference.

#### Restore by PIM product data

Use this approach when your integration knows the Customer's Canvas product ID and the product version where the design was created. Use the SKU variant when your catalog identifies the variant by SKU.

```typescript
await editor.init({
  configVersion: 2,
  input: {
    designId: "<design_id>",
    productId: <product_id>,
    productVersionId: <product_version_id>,
    sku: "ABC-1234",
  },
  integration: { /* ... */ },
});
```

Use the variant ID variant when your catalog identifies the variant by its `productVariantId`.

```typescript
await editor.init({
  configVersion: 2,
  input: {
    designId: "<design_id>",
    productId: <product_id>,
    productVersionId: <product_version_id>,
    productVariantId: <product_variant_id>,
  },
  integration: { /* ... */ },
});
```

#### Restore by product reference

Use this approach when your integration loads products through a product reference, for example, in a standard e-commerce integration. Pass `productReferenceId` together with the saved design ID. In this case, you don't need `productVersionId` — the editor resolves the product and its version through the product reference.

Use the SKU variant when your catalog identifies the variant by SKU.

```typescript
await editor.init({
  configVersion: 2,
  input: {
    productReferenceId: "<product_reference_id>",
    designId: "<design_id>",
    sku: "ABC-1234",
  },
  integration: { /* ... */ },
});
```

Use the variant ID variant when your catalog identifies the variant by its `productVariantId`.

```typescript
await editor.init({
  configVersion: 2,
  input: {
    productReferenceId: "<product_reference_id>",
    designId: "<design_id>",
    productVariantId: <product_variant_id>,
  },
  integration: { /* ... */ },
});
```

Both restore approaches also accept the optional `designVariantTemplateId` field. Pass the ID of the public template from which the design variant was created to indicate to the editor which design variant the user selected.

## Respond to lifecycle events

The host application renders the controls for product options, design variants, and design elements. The editor provides the current state through its public API and notifies the host application when that state changes through `CustomEvent` objects whose payload is in the `detail` property.

Subscribe to the events before calling `init()`. The same events fire when the current product is later changed through `openProduct()`.

| Event | `detail` | Use it to |
| --- | --- | --- |
| `load` | — | signal that the product and its initial design are ready |
| `productchanged` | `Product` | rebuild product controls and re-read options |
| `variantchanged` | `ProductVariant` | rebuild the design variant selector |
| `optionchoiceschanged` | option choices | refresh the displayed option selection |
| `designvariantchanged` | `DesignVariant` | rebuild the controls for the current design |
| `surfacechanged` | `Surface` | rebuild the controls for the current surface |
| `addtocart` | `LineItem` | submit the finished customization |
| `error` | `Error` | show an error message or reset the loading state |

```typescript
import type { DesignVariant, Product, ProductVariant } from "@aurigma/workflow-elements/headless-editor";

editor.addEventListener("productchanged", event => {
  const product = (event as CustomEvent<Product>).detail;
  const choices = editor.getChoices();

  renderProductOptions(product.options, choices);
});

editor.addEventListener("variantchanged", event => {
  const variant = (event as CustomEvent<ProductVariant>).detail;

  renderDesignVariants(variant.designVariants);
});

editor.addEventListener("designvariantchanged", event => {
  const designVariant = (event as CustomEvent<DesignVariant>).detail;

  refreshDesignControls(designVariant);
});
```

Here, `renderProductOptions()`, `renderDesignVariants()`, and `refreshDesignControls()` represent functions implemented by your application.

## Finish a customization session

When the customer finishes, the editor saves the current design and produces a line item. The line item identifies the selected product and variant and contains the private design ID required to create a Customer's Canvas project.

The event-driven flow matches the other editors. Subscribe to `addtocart`, and call `addToCart()` from the host application's button.

```typescript
import type { LineItem } from "@aurigma/workflow-elements/headless-editor";

editor.addEventListener("addtocart", async event => {
  const lineItem = (event as CustomEvent<LineItem>).detail;

  await submitLineItem(lineItem);
});

document.querySelector("#add-to-cart")?.addEventListener("click", async () => {
  await editor.addToCart();
});
```

`addToCart()` saves the current design and emits `addtocart` with the resulting line item in `event.detail`. It does not communicate with an e-commerce platform itself. Your handler creates a Customer's Canvas project and adds the item to the storefront cart.

If the event flow is not convenient, call `getLineItem()` and process its result directly.

```typescript
const lineItem = await editor.getLineItem();

await submitLineItem(lineItem);
```

`getLineItem()` also saves the current design, but returns the line item without emitting `addtocart`. Use either the event-driven or the direct-return flow for a single user action to avoid processing the same customization twice.

The `LineItem` carries product and variant identifiers, the SKU, the quantity, and the saved state in `properties`:

- `_stateId` — the identifiers of the saved design states.
- `_userId` — the storefront user ID.
- `_hidden` — internal values such as the design snapshot and generated images.

Send this line item to a trusted backend and create a Customer's Canvas project there, as shown in the [quick start](https://customerscanvas.com/docs/hub/dev-center/frontend-integration/workflow-elements/headless-editor/quick-start.md). See [Projects](https://customerscanvas.com/docs/hub/dev-center/concepts/projects.md) for the project concept.

## Update configuration after initialization

Call `update()` to change settings or resources without a full reinitialization. `update()` receives the same shape as `init()`, but you should only pass the settings or resources you want to replace.

```typescript
editor.update({
  settings: {
    viewer: {
      itemManipulationEnabled: false,
    },
  },
});
```

Updating the product input is not intended because it would discard the current customization progress. To open another product, use `openProduct()`, which is covered in [Products, options, and variants](https://customerscanvas.com/docs/hub/dev-center/frontend-integration/workflow-elements/headless-editor/products-options-variants.md).

## Reference

- IHeadlessEditorInitArgs — the full configuration shape.
- HeadlessEditorInput — the input types.
- LineItem — the line item model.
- IHeadlessEditor — the component method reference.
