---
title: "Introducing the IFrame API"
canonical: https://customerscanvas.com/docs/cc6/handling-product-customization.htm
archived: true
applies_to:
  - product: "Customer's Canvas On-Premises"
  - product: "Design Editor"
    versions: ["6.15.0", "6.x (may differ)"]
---

**You’re viewing outdated documentation.** [View the latest Customer’s Canvas On-Premises documentation](https://customerscanvas.com/docs/on-premises/).

# Introducing the IFrame API

**Applies to:** Customer's Canvas On-Premises; Design Editor 6.15.0, 6.x (may differ) · **Archived documentation**
 
This topic dwells on how to handle product customization using the Customer's Canvas IFrame API. Here, we discuss such tasks as saving products, approving designs, and rendering the hi-res output. The Customer's Canvas IFrame API provides three methods to perform these tasks: [Editor.saveProduct](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.saveproduct.htm.md), [Editor.getProofImages](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.getproofimages.htm.md), and [Editor.finishProductDesign](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.finishproductdesign.htm.md).
 
In this topic, you can find the sample demonstrating how to handle product loading, save products, display proof images, reload products for further editing, and provide links for the hi-res output.
 
After the API is initialized, you can save the product, get proof images for displaying them to the user in the approval process, and render the hi-res output.
 
## Saving a Product
  
The [Editor.saveProduct](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.saveproduct.htm.md) method allows for saving a product's current state. You may call this method while a user is customizing the product, for example, by adding the **Save** button to the design page as the sample shows. Also, you can implement the autosave feature by calling this method after equal periods of time using a timer.
 
If you want to allow your users to name saved products or rewrite products, you can use the **stateId** optional parameter of **saveProduct**. When you omit this parameter, **saveProduct** creates file names using unique product state identifiers (GUID). As a result, this method returns the product state ID, user ID, and the return-to-edit URL. Having these details, you can reload the product by its name.
 
The **saveProduct** method returns a Promise, whose the **onFulfilled** callback function accepts an object implementing the [ISaveProductResult](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.isaveproductresult.htm.md) interface. This interface has the following properties:
 
- **stateId**: the product state identifier; it appears as xxxxxxxx-yyyy-xxxx-yyyy-xxxxxxxxxxxx by default. You can use this value to load the product into the editor, as it is described in Loading a Product State into the Editor.
- **userId**: the user identifier. If you specified a user identifier in the editor configuration using the **IConfiguration.userId** property, then you get the identifier presented here. Otherwise, you get the default user identifier.
- **returnToEditUrl**: the return-to-edit URL. This property is provided for backward compatibility only; it utilizes the old API based on query strings. To learn on how to perform the same functionality using the IFrame API, see the description of Loading a Product State into the Editor.

```
//Saving a product to the StateFileName.st file.
editor.saveProduct("StateFileName")
    //If the product is saved correctly.
    .then(function (result) {
        stateId = result.stateId;
        returnToEditUrl = result.returnToEditUrl;
        ...
    })
    //If there was an error thrown when saving the product.
    .catch(function (error) {
        console.error("Saving product failed with exception: ", error);
    });
```
 
**Note** 
All the API functions discussed in this topic should be called in a page context where the Customer's Canvas editor is hosted.
 
## Getting Proof Images
  
As a part of the process when users approve results of their work (or return to the editor to make some last-minute changes), you need to show them proof images displaying customized products. At this stage, we do not need hi-res output as the product is not finalized yet. So, just call the [getProofImages](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.getproofimages.htm.md) method after a user has finished customizing a product.
 
Customer's Canvas allows you to render three types of proof images: a page thumbnail, a real-size page preview, and product thumbnails as a multi-page PDF. All of these proof images are 72 DPI. To configure proof images, you can pass the following options to **getProofImages**:
 
- A page thumbnail:
    - **maxHeight** is the maximum height of thumbnails.
    - **maxWidth** is the maximum width of thumbnails.
- A real-size page preview:
    - **generateLargePreviews** enables real-size proof images.
    - **largePreviewMaxHeight** is the maximum height (by default, the surface height is applied).
    - **largePreviewMaxWidth** is the maximum width (by default, the surface width is applied).
- A product preview:
    - **generateProductProof** enables a preview of all product pages in a multi-page PDF file.

All these properties are optional. By default, this method renders a page thumbnail. You can pass these properties as follows:
 
```
//Getting links to proof images.
editor.getProofImages({maxHeight: 640, maxWidth: 640, pregeneratePreviewImages: true})
    //Handling the results.
    .then(function (result) { ... })
    .catch(function (error) { ... });
```
 
To specify the required size, you can pass the maximum width and height. If you omit these parameters, the default value of `500` will be applied. This method proportionally resizes proof images according to **maxHeight** and **maxWidth**. For example, if the maximum width and height are both set to `640` pixels, then a `1280 x 960` px image will be resized to `640 x 480` px. The previous example sets the width and height of a proof image to `640` pixels.
 
You can also pass the **pregeneratePreviewImages** option to start generating proof images in the background when running this method. By default, **getProofImages** only returns links to proof images and starts generating these images when a request to download them is received.
 
This method returns a Promise, whose the **onFulfilled** callback function accepts an object implementing the [IProofResult](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.iproofresult.htm.md) interface. This interface has the only property:
 
- **proofImageUrls**: the array of temporary links to proof images. The [getProofImages](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.getproofimages.htm.md) method creates [state files](https://customerscanvas.com/docs/cc6/creating-templates-without-Photoshop.htm.md) in the cache, so these links become invalid after a cache cleanup. You can get permanent links to proof images, using the [finishProductDesign](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.finishproductdesign.htm.md) method.

```
//Getting links to proof images.
editor.getProofImages()
    //If the links to proof images were generated successfully.
    .then(function (result) {
        proofImageUrls = result.proofImageUrls;
        ...
    })
    //If there was an error thrown while getting links to proof images.
    .catch(function (error) {
        console.error("Getting proof images failed with exception: ", error);
    });
```
 
When you generate product previews in PDF files and allow your users to directly download these files using the links returned by this method, you can pass the **filename** query parameter that enables the download and customizes the resulting file name. For example, to download a `booklet.pdf` file:
 `https://example.com/api/rendering/GetProofImage/JohnWood/06047be9-78fb-4b89-98c5-8b1712ba1b51/-1_-1.pdf?filename=booklet `
 
## Rendering the Hi-res Output
  
The last step, after a user has approved the design, is rendering the hi-res output. To perform this, call the [finishProductDesign](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.finishproductdesign.htm.md) method. This method saves the current product state and returns links to the hi-res output. The **finishProductDesign** method returns a Promise, whose the **onFulfilled** callback function accepts an object implementing the [IFinishDesignResult](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.ifinishdesignresult.htm.md) interface. This interface has the following properties:
 
- **hiResOutputUrls**: the array of links to the hi-res output.
- **proofImageUrls**: the array of permanent links to proof images. The [finishProductDesign](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.finishproductdesign.htm.md) method creates [state files](https://customerscanvas.com/docs/cc6/creating-templates-without-Photoshop.htm.md) in the `~\userdata\<someUserId>\states\` folder, so these proof images can be regenerated if a cache cleanup occurs.
- **returnToEditUrl**: the return-to-edit URL. This property is provided for backward compatibility only; it utilizes the old API based on query strings. To learn how to perform the same functionality using the IFrame API, see the description of Loading a Product State into the Editor.
- **stateId**: the product state identifier; it appears as xxxxxxxx-yyyy-xxxx-yyyy-xxxxxxxxxxxx by default. You can use this value to load the product into the editor, as it is described in Loading a Product State into the Editor.
- **userId**: the user identifier. If you specified a user identifier in the editor configuration using the **IConfiguration.userId** property, then you get the identifier presented here. Otherwise, you get the `default` user identifier.
- **userChanges**: the object containing information about the product changes made by a user.
- **boundsData**: the bounding rectangle of all items in the product, in points. For more information, see the [Calculating Area of Design Layers](https://customerscanvas.com/docs/cc6/measuring-layers.htm.md) topic.
- **violationWarningData**: the array of items having preflight problems.

```
//Completing product customization.
editor.finishProductDesign()
    //If product customization is completed successfully.
    .then(function (result) {
        hiResOutputUrls = result.hiResOutputUrls;
        proofImageUrls = result.proofImageUrls;
        stateId = result.stateId;
        returnToEditUrl = result.returnToEditUrl;
        boundsData = result.boundsData;
        userChanges = result.userChanges;
        ...
    })
    //If there was an error thrown when completing product customization.
    .catch(function (error) {
        console.error("Completing product customization failed with exception: ", error);
    });
```
 
The **userChanges** object is not as simple and straightforward as the other data fields returned by this callback, so it needs clarification. As pointed out before, this object contains text entered by the user in text elements. That means you can get all text input on the server side, which allows for providing the user with custom default values for text layers, such as name, phone, address, etc. To implement this, your system should store values entered by the user in a database and pass them off as defaults to other templates loaded by the user. For example, if one template has an `address` field, you can store the entered value and pre-populate `address` fields in other templates loaded by the user.
 
The **userChanges** object implements the [IUserChanges](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.iuserchanges.htm.md) interface and has the following structure:
 
```
{
    inStringPlaceholders: [ {
            name: "placeholder_name",
            usersValue: "placeholder_value"
        },
        ...
    ],
    mockups: [ {
            mockup: "mockup_name",
            surfaceName: "surface_name"
        },
        ...
    ],
    texts: [ {
            name: "layer_name",
            usersValue: "layer_text",
            isNewItem: "new_or_changed",
            isRemovedItem: "removed_or_changed"
        },
        ...
    ],
    surfaces: {
        surface_0: {
            inStringPlaceholders: [ {
                    name: "placeholder_name",
                    usersValue: "placeholder_value"
                },
                ...
            ],
            texts: [ {
                    name: "layer_name",
                    usersValue: "layer_text",
                    isNewItem: "new_or_changed",
                    isRemovedItem: "removed_or_changed"
                },
                ...
            ]
        }
    }
}
```
 
Here, the **inStringPlaceholders** and **texts** arrays contain text entered by the user in text layers. For each text layer that was changed by the user, the corresponding object contains its name and new value. Elements in **texts** contain the **isNewItem** property, which is `true` for new layers and `false` for changed layers, and the **isRemovedItem** property, which becomes `true` if the user deleted the text layer. Customer's Canvas returns these arrays twice: at the top level of **IUserChanges** and in the **surfaces** object containing changes on a per-surface basis. In the previous example, **surface\_0** is the surface name. Elements of the **mockups** array contain mockup names that the user set up for the surfaces. For information about text placeholders, see the [In-String Placeholders and Text Validation](https://customerscanvas.com/docs/cc6/in-string-placeholders-and-text-validation.htm.md) topic. Text layers and mockups are discussed in the [Point and Paragraph Text](https://customerscanvas.com/docs/cc6/point-paragraph-rich-text.htm.md) and [Mockups](https://customerscanvas.com/docs/cc6/mockups.htm.md) topics, correspondingly.
 
As an optional parameter, you can pass the following object to **finishProductDesign**.
 
```
editor.finishProductDesign({fileName: "envelope", proofMaxHeight: 640, proofMaxWidth: 640,
                            stateId: "StateFileName", pregeneratePreviewImages: true})
    .then(function (result) { ... })
    .catch(function (error) { ... });
```
 
All properties of this object are optional. You can pass the **filename** to customize the resulting file name. This name is only used when you allow the end-user to directly download hi-res files using the links returned by the **finishProductDesign** method. If you omit the argument and try downloading a hi-res file, it will always have the `result.ext` name where `.ext` stands for the actual file extension for the configured output format (it will be `result.pdf` for the PDF format). This behavior can be changed using the argument and `result` will be replaced with whatever is passed in it. If Customer's Canvas is set up to produce multiple hi-res files, the same file name will be used for all of them, and the user will have the option to rename them on the client side when saving in the browser. You can find the information on how to set the type of the hi-res output in the [Configuring Hi-res Output](https://customerscanvas.com/docs/cc6/configuring-high-resolution-and-proof-images.htm.md) topic.
 
If you want to name or rewrite saved state files, you can use the **stateId** optional parameter. When you omit this parameter, **finishProductDesign** creates file names using unique product state identifiers.
 
This method takes the same options for proof images as getProofImages.
 
## Loading a Product State into the Editor
  
State files are internal Customer's Canvas files, which are created when products are saved via the [Editor.saveProduct](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.saveproduct.htm.md) or [Editor.finishProductDesign](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.finishproductdesign.htm.md) methods.
 
State files are located in user subfolders and have names like `aaaaaaaa-bbbb-cccc-xxxx-yyyyyyyyyyyy.st` by default, where **aaaaaaaa-bbbb-cccc-xxxx-yyyyyyyyyyyy** is the unique value of the **stateId** property returned by any of the methods mentioned above. So, to load a saved product into the editor, you should pass a state file name without an extension and user identifier to the [loadEditor](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.loadeditor.htm.md) method as follows:
 
```
//The finishing of the product customization.
editor.finishProductDesign()
    .then(function (result) {
        ...
        //The unique identifier of the saved product state.
        stateId = result.stateId;
        //The user identifier.
        userId = result.userId;
        ...
    })
    .catch(function (error) {
        ...
    });
...
//The loading of the previously saved product.
CustomersCanvas.IframeApi.loadEditor(iframe, stateId, { userId: userId });
```
 
## The Sample
  
The code sample in this section represents a three-step wizard that contains the design page, the approval page, and the finish page. Its workflow includes the following steps:
 
1. The system loads a product template.
2. A user creates a design.
3. The user can save the design using the **Save** button. In this case, the system calls the **saveProduct** method.
4. The user initializes the transfer to the approval page. The system displays proof images.
5. The user can return to editing the product. In this case, the system restores the product using the state ID received in the previous step.
6. The user approves the design. The system displays the finish page containing the link to the hi-res output.

To make this sample work do the following:
 
1. Copy and paste the code to the `index.html` file.
2. Replace `example.com` with the name of the site where your Customer's Canvas instance is hosted and save the file.
3. Open `index.html` in a browser.

Alternatively, you can [download this sample](https://customerscanvas.com/docs/cc6/samples/HandlingProductCustomization.zip) as a zip file and extract its content into the root folder of Customer's Canvas.
 
```html
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta charset="utf-8" />
    <title>Business cards - The sample page - Customer's Canvas</title>

    <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.1.min.js">
    </script>
    <link href="index.css" rel="stylesheet" />
    <!-- The IFrame API script. IMPORTANT! Do not remove or change the ID. -->
    <script id="CcIframeApiScript" type="text/javascript" src="http://example.com/Resources/Generated/IframeApi.js">
    </script>

    <script>
        $(function () {
            //Defining the product.
            var productDefinition = { surfaces: ["stamp"] };

            //Defining the editor configuration.
            var configuration = { widgets: { FinishButton: { mode: "Disabled" } }};

            var editorFrame = $("#editorFrame");

            var editor = null;

            function handleError(error, message) {
                console.error(message, error);
                return error;
            };

            var loadData = null;

            //Loading a product into the editor.
            CustomersCanvas.IframeApi.loadEditor(editorFrame[0], productDefinition, configuration)
                //If the editor has been successfully loaded.
                .then(function (e) {
                    editor = e;
                })
                //If there was an error thrown when loading the editor.
                .catch(function (error) {
                    loadData = handleError(error, "Load failed with exception: ");
                });

            var currentPage = "design";

            //Object containing data generated by getProofImages.
            var approveData = null;

            //Object containing data generated by finishProductDesign.
            var renderData = null;

            //Object containing data generated by saveProduct.
            var saveData = null;
 
            //Saving the product.
            $("#editorPage #saveButton").click(function () {
 
                editor.saveProduct()
                    //If the product has been successfully saved.
                    .then(function (result) {
                        //Saving the product state info.
                        saveData = result;
                    })
                    //If there was an error thrown when saving the product.
                    .catch(function (error) {
                        saveData = handleError(error, "Save failed with exception: ");
                    });
            });

            //Getting links to proof images.
            $("#editorPage #nextButton").click(function () {

                editor.getProofImages()
                    //If proof images have been successfully received.
                    .then(function (result) {
                        //Saving proof images info.
                        approveData = result;

                        //Go to the approval page.
                        goToPage("approve");
                    })
                    //If there was an error thrown while getting the proof images.
                    .catch(function (error) {
                        approveData = handleError(error, "Getting proof images failed with exception: ");
                    });
            });

            //Finishing the product customization.
            $("#approvePage #approveButton").click(function () {

                editor.finishProductDesign()
                    //If the product customization has been successfully completed.
                    .then(function (result) {
                        //Saving hi-res output info.
                        renderData = result;

                        //Go to the finish page.
                        goToPage("finish");
                    })
                    //If there was an error thrown while finishing the product customization.
                    .catch(function (error) {
                        renderData = handleError(error, "Product customization completion failed with exception: ");
                    });
            });

            //Opening a new product in the designer.
            $("#finishOrderPage #newDesign").click(function () {
                editor.revertProduct();
                goToPage("design");
            });

            //Reopening the product in the designer.
            $("#approvePage #lnkEditAgain").click(function () {
                goToPage("design");
            });

            //Initializing the approval and finish pages with the product info.

            //Initializing the approval page with links to the proof images.
            function setApprovePageData() {
                var previewElements = $("#approvePage .previewImg").attr("src", "");
                for (var i = 0; i < approveData.proofImageUrls.length && i < previewElements.length; i++) {
                    previewElements[i].setAttribute("src", approveData.proofImageUrls[i]);
                }
            };

            //Initializing the finish page with the print-ready output URL and a link for reopening the product in the designer for further editing.
            function setFinishPageData() {
                $("#finishOrderPage #hiResLink").attr("href", renderData.hiResOutputUrls[0]);
            };

            //The wizard navigation.

            function goToPage(to) {
                setupPage(to);
            }
            window.onpopstate = function (e) { setupPage(e.state); }

            var workflowPages = {
                "design": { elements: $("#editorPage") },
                "approve": { elements: $("#approvePage"), setData: setApprovePageData },
                "finish": { elements: $("#finishOrderPage"), setData: setFinishPageData }
            }

            function setupPage(page) {
                var destPageData = workflowPages[page];

                if (typeof destPageData.setData === "function")
                    destPageData.setData();

                workflowPages[currentPage].elements.fadeOut(function () {
                    destPageData.elements.show();

                    currentPage = page;
                });
            }
        })
    </script>
</head>

<body>
    <div id="wrapper">
        <div id="content">
            <!-- The design page -->
            <div id="editorPage" class="area">
                <div id="iframeWrapper">
                    <iframe id="editorFrame" width="100%" height="800px"></iframe>
                </div>
                <div id="saveAndNextButtonsWrapper">
                    <!-- The Save button -->
                    <input id="saveButton" type="button" class="btn btn-info btn-lg" value="Save" />
                    <!-- The Finish design button -->
                    <input id="nextButton" type="button" class="btn btn-success btn-lg" value="Finish design >" />
                </div>
            </div>

            <!-- The approval page -->
            <div id="approvePage" class="area" style="display: none">
                <div class="container-fluid">
                    <h1>Approve Your Product</h1>
                    <!-- proof images -->
                    <img class="previewImg" id="preview" />
                    <img class="previewImg" id="previewPage2" />
                </div>
                <p>
                    <div id="approveButtonWrapper">
                        <input id="approveButton" type="button" class="btn btn-success btn-lg" value="Approve >" />
                    </div>
                </p>
                <div class="return">
                    <a id="lnkEditAgain">&lt; I want to make some changes</a>
                </div>
            </div>

            <!-- The finish page -->
            <div id="finishOrderPage" class="area" style="display: none">
                <h1 class="">Your Product is Ready</h1>
                <!-- The link for downloading the hi-res output. -->
                The print-ready file can be downloaded from <a id="hiResLink">this link</a>
                <div class="right">
                    <input id="newDesign" type="button" class="btn btn-info btn-lg" value="< New design" />
                </div>
            </div>
        </div>
    </div>
</body>
</html>
```
 
Additionally, you can copy the following CSS snippet and save it to the `index.css` file in the same folder where the `index.html` is placed. These styles will give your page a more user-friendly interface, however, it is not mandatory.
 
```
body {
    font: 12px/18px Arial, "Helvetica CY", "Nimbus Sans L", sans-serif;
    height: 100%;
    min-width: 960px;
}

.area,
.fluid {
    position: relative;
}

.area {
    width: 940px;
    margin: 0 auto;
}

#editorFrame {
    display: block;
    border: 0;
}

#content {
    padding: 0px 0px 100px;
}

#saveAndNextButtonsWrapper {
    text-align: right;
    margin-top: 10px;
}

#approvePage {
    font: 14px Tahoma,Arial,Helvetica,sans-serif;
}

#approvePage .previewImg {
    max-width: 600px;
    max-height: 600px;
    box-shadow: rgba(100, 100, 100, 0.8) 0 0 3px;
    margin-top: 1em;
}

#approvePage .container-fluid {
    text-align: center;
}

#approvePage .agree {
    border: dashed 2px #f00;
    text-align: left;
    padding: 4px 16px 16px;
    margin-top: 15px;
}

#approvePage #approveButtonWrapper {
    text-align: right;
}

#approvePage .return{
    margin-top: 20px;
    font-size: 14pt;
}

#approvePage .return a, a:active, a:focus {
    border-bottom: 1px solid rgb(169, 227, 149);
    color: rgb(100, 188, 70);;
}

#approvePage .return a:hover {
    color: rgb(43, 121, 16);
    text-decoration: none;
    cursor: pointer;
}

#finishOrderPage h3 {
    text-align: justify;
}

#finishOrderPage h1 {
    text-align: center;
}

#finishOrderPage .right {
    text-align: right;
}

#finishOrderPage ul {
    list-style-type: disc;
}
```
 
## See Also
  
### Manual
 
- [Integration with E-commerce Overview](https://customerscanvas.com/docs/cc6/integration-with-e-commerce.htm.md)
- [Configuring High Resolution and Proof Images](https://customerscanvas.com/docs/cc6/configuring-high-resolution-and-proof-images.htm.md)
- [Creating and Editing Templates in Customer's Canvas](https://customerscanvas.com/docs/cc6/creating-templates-without-Photoshop.htm.md)

### IFrame API Reference
 
- [Editor.saveProduct](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.saveproduct.htm.md)
- [ISaveProductResult interface](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.isaveproductresult.htm.md)
- [Editor.getProofImages](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.getproofimages.htm.md)
- [IProofResult interface](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.iproofresult.htm.md)
- [Editor.finishProductDesign](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.finishproductdesign.htm.md)
- [IFinishDesignResult interface](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.ifinishdesignresult.htm.md)
- [IUserChanges interface](https://customerscanvas.com/docs/cc6/customerscanvas.iframeapi.editor.iuserchanges.htm.md)

### Downloads
 
- [Code sample](https://customerscanvas.com/docs/cc6/samples/HandlingProductCustomization.zip)
