Back to Website
Show / Hide Table of Contents

Generating previews and proofs for private designs

  • 25 minutes to read

When working with user-generated designs created either in an editor or programmatically, a common task is visualizing these designs. You may need to display the design content on screen, which can vary depending on the task at hand. This article covers various scenarios for generating images such as:

  • Thumbnails
  • Previews
  • Proofs

Thumbnails

Thumbnails are suitable when you need to display a list of designs, for example, created by a user. In this case, you need to show the design template without a mockup, and no on-the-fly image modification is expected. Speed is crucial, and if a thumbnail has already been created, it will be automatically retrieved from the cache.

For private user designs, you can use one of the following endpoints of the Asset Processor API:

  • Get the thumbnail file as binary data:

    GET /api/processor/v1/private-designs/{id}/preview/{namespace}/{name}/{width}x{height}

  • Get a link to the thumbnail file:

    GET /api/processor/v1/private-designs/{id}/preview/{namespace}/{name}/{width}x{height}/url

Note

Similar endpoints are available for other asset types, both private and public images, fonts, and public designs.

If you obtain a file link, it can be opened anonymously, making it easy to insert into an <IMG> tag.

To use these endpoints, you need to provide at least the following data:

  • Design identifier
  • Image dimensions
  • Name and namespace

Optionally, you can specify the format (JPEG or PNG) and other parameters discussed below.

Namespace and Name

Namespace and name allow Customer's Canvas to store previews for different contexts. The namespace is the name of your application. Different applications working with your data can have previews with different settings.

The name allows you to specify the purpose of the preview. For example, you might need small thumbnails for a list of designs and large previews for a detailed view.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
  GET "https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/200x200" \
  -H  "accept: application/octet-stream" \
  -H  "Authorization: Bearer <TOKEN>"
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/200x200
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var previewNamespace = "portal"; // Put your real preview name space here
var previewName = "thumbnail"; // Put your real preview name here
var width = 200; // Put your real preview width here
var height = 200; // Put your real preview height here
var response = await privateDesignProcessorApiClient.PreparePreviewAsync(designId, previewNamespace, previewName, width, height);
var stream= await response.Content.ReadAsStreamAsync();
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var previewNamespace = "portal"; // Put your real preview name space here
var previewName = "thumbnail"; // Put your real preview name here
var width = 200; // Put your real preview width here
var height = 200; // Put your real preview height here
var response = await _privateDesignProcessorApiClient.preparePreview(designId, previewNamespace, previewName, width, height);
$designId = "6698b073306ecea21de39b11"; // Put your real design ID here
$previewNamespace = "portal"; // Put your real preview name space here
$previewName = "thumbnail"; // Put your real preview name here
$width = 200; // Put your real preview width here
$height = 200; // Put your real preview height here
$response = $privateDesignProcessorApiClient->privateDesignProcessorPreparePreview($designId, $previewNamespace, $previewName, $width, $height);

Preview for Multi-page Designs

The number of surfaces in the design can be determined by reading the design metadata from Asset Storage using the endpoint GET /api/storage/v1/private-designs with filtering by owner name under which it was created (see the Storefront User API).

For example, to get a list of private designs created by a user with ID crisford.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
  GET "https://api.customerscanvashub.com/api/storage/v1/private-designs?ownerId=crisford" \
  -H  "accept: application/json" \
  -H  "Authorization: Bearer <TOKEN>"
GET https://api.customerscanvashub.com/api/storage/v1/private-designs?ownerId=crisford
var ownerId = "crisford"; // Put your user ID here
var designList = await privateDesignsApiClient.GetAllAsync(ownerId: ownerId);
let ownerId = "crisford"; // Put your user ID here
let designList = await _privateDesignsApiClient.getAll(null, null, null, null, null, null, null, null, null, null, ownerId);
$ownerId = "crisford"; // Put your user ID here
$designList = $privateDesignsApiClient->privateDesignsGetAll(null, null, null, null, null, null, null, null, null, null, $ownerId);

From the response, get the length of the metadata.surfaces array to obtain the number of pages for every private design. Dy default, only the first surface is rendered. If the design consists of multiple surfaces, you can use the surfaceIndex parameter to request a preview for each surface and return URLs to them. For example, for three-page design:

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
# Loop through pages 0, 1, and 2
for index in 0 1 2; do
  response=$(curl -s -X GET "https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/200x200/url?surfaceIndex=$index" \
  -H "accept: text/plain" \
  -H "Authorization: Bearer <TOKEN>")
  echo "Page $index: $response"
done
# Example requests for pages 0, 1, and 2
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/200x200/url?surfaceIndex=0
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/200x200/url?surfaceIndex=1
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/200x200/url?surfaceIndex=2
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var previewNamespace = "portal"; // Put your real preview name space here
var previewName = "thumbnail"; // Put your real preview name here
var width = 200; // Put your real preview width here
var height = 200; // Put your real preview height here

// Loop through pages 0, 1, and 2
for (int index = 0; index < 3; index++)
{
    var response = await privateDesignProcessorApiClient.PreparePreviewUrlAsync(designId, previewNamespace, previewName, width, height, index);
    var url = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"Page {index}: {url}");
}
let designId = "6698b073306ecea21de39b11"; // Put your real design ID here
let previewNamespace = "portal"; // Put your real preview name space here
let previewName = "thumbnail"; // Put your real preview name here
let width = 200; // Put your real preview width here
let height = 200; // Put your real preview height here

// Loop through pages 0, 1, and 2
for (let index = 0; index < 3; index++) {
    let response = await _privateDesignProcessorApiClient.preparePreviewUrl(designId, previewNamespace, previewName, width, height, index);
    let url = await response.text();
    console.log(`Page ${index}: ${url}`);
}
$designId = "6698b073306ecea21de39b11"; // Put your real design ID here
$previewNamespace = "portal"; // Put your real preview name space here
$previewName = "thumbnail"; // Put your real preview name here
$width = 200; // Put your real preview width here
$height = 200; // Put your real preview height here

// Loop through pages 0, 1, and 2
for ($index = 0; $index < 3; $index++) {
    $response = $privateDesignProcessorApiClient->privateDesignProcessorPreparePreviewUrl($designId, $previewNamespace, $previewName, $width, $height, $index);
    $url = $response->getBody()->getContents();
    echo "Page $index: $url\n";
}

Stub Content

If the design contains unfilled image or text placeholders, you may want to display them or show empty space instead. To control this, use the stub query parameter. Depending on its value, the preview may appear as follows:

Design with stubs

Dy default, unfilled stubs are rendered on previews. This is how you can hide them:

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
  GET "https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/300x200/url?stub=false" \
  -H  "accept: text/plain" \
  -H  "Authorization: Bearer <TOKEN>"
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/300x200/url?stub=false
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var previewNamespace = "portal"; // Put your real preview name space here
var previewName = "thumbnail"; // Put your real preview name here
var width = 300; // Put your real preview width here
var height = 200; // Put your real preview height here
var stub = false; // Hide unfilled stubs
var response = await privateDesignProcessorApiClient.PreparePreviewUrlAsync(designId, previewNamespace, width, height, stub: stub);
var url = await response.Content.ReadAsStringAsync();
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var previewNamespace = "portal"; // Put your real preview name space here
var previewName = "thumbnail"; // Put your real preview name here
var width = 300; // Put your real preview width here
var height = 200; // Put your real preview height here
var stub = false; // Hide unfilled stubs
var response = await _privateDesignProcessorApiClient.preparePreviewUrl(designId, previewNamespace, width, height,  null, stub);
var url = await response.text();
$designId = "6698b073306ecea21de39b11"; // Put your real design ID here
$previewNamespace = "portal"; // Put your real preview name space here
$previewName = "thumbnail"; // Put your real preview name here
$width = 300; // Put your real preview width here
$height = 200; // Put your real preview height here
$stub = false; // Hide unfilled stubs
$response = $privateDesignProcessorApiClient->privateDesignProcessorPreparePreviewUrl($designId, $previewNamespace, $previewName, $width, $height, null, $stub);
$url = $response->getBody()->getContents();

Force Preview Generation

The preview image is cached on the first access. If you try to get a preview for the same combination of designId, namespace, and name again, the Asset Processor API will not recreate these files but will return the cached versions to you.

When the design is modified, Customer's Canvas automatically resets the preview. Thus, you are guaranteed to get an up-to-date image. However, this is sometimes not enough. For example, if you change the image dimensions or the stub flag for a given namespace and name, you will still get the old preview until the design file is modified. In such situations, you can add the force flag to reset the previously created preview before obtaining a new one.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
  GET "https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/160x120/url?force=true" \
  -H  "accept: text/plain" \
  -H  "Authorization: Bearer <TOKEN>"
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/160x120/url?force=true
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var previewNamespace = "portal"; // Put your real preview name space here
var previewName = "thumbnail"; // Put your real preview name here
var width = 160; // Put your real preview width here
var height = 120; // Put your real preview height here
var force = true; // Regenerate the preview image
var response = await privateDesignProcessorApiClient.PreparePreviewUrlAsync(designId, previewNamespace, width, height, force: force);
var url = await response.Content.ReadAsStringAsync();
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var previewNamespace = "portal"; // Put your real preview name space here
var previewName = "thumbnail"; // Put your real preview name here
var width = 160; // Put your real preview width here
var height = 120; // Put your real preview height here
var force = true; // Regenerate the preview image
var response = await _privateDesignProcessorApiClient.preparePreviewUrl(designId, previewNamespace, width, height, null, null, null, force);
var url = await response.text();
$designId = "6698b073306ecea21de39b11"; // Put your real design ID here
$previewNamespace = "portal"; // Put your real preview name space here
$previewName = "thumbnail"; // Put your real preview name here
$width = 160; // Put your real preview width here
$height = 120; // Put your real preview height here
$force = true; // Regenerate the preview image
$response = $privateDesignProcessorApiClient->privateDesignProcessorPreparePreviewUrl($designId, $previewNamespace, $previewName, $width, $height, null, null, null, $force);
$url = $response->getBody()->getContents();

Product Preview

In some scenarios, obtaining thumbnails through the Asset Processor API may not be suitable, for example:

  • When you want to visualize the design on a mockup.
  • When you want to populate the template with data.

We will refer to an image that shows how a specific manufactured copy of the product will look as a "preview." To obtain one, you need to use endpoints of the Design Atoms API:

  • Render the design preview as binary data:

    POST /api/atoms/v1/designs/render-preview

  • Save the rendered preview as a resource and retrieve its details:

    POST /api/atoms/v1/designs/render-preview/to-resource

The first endpoint recreates the product preview on each call, and you receive a resulting file as binary data. The other endpoint saves every result as a resource in asset storage and returns the ID and URL of this resource to you.

Warning

Save multiple resources to storage carefully, as you can quickly accumulate a significant number of such images. If there is no objective need to store these files, delete them manually or configure retention policy to manage their lifetime.

Simple Preview

The simplest use case is obtaining a plain flat product image. You can request an image of the desired size and format, and specify which surface to use. Provide such parameters as a JSON object in the request body, for example:

{
  "designId": "6698b073306ecea21de39a00",
  "ownerId": "crisford",
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 200,
    "height": 200,
    "fileFormat": "Jpeg"
  }
}

Here, the width and height are set to 500 pixels by default, and the default file format is "Png". Unlike the Asset Processor API where you could omit the ownerId, the Design Atoms API requires specifying both ownerId and designId for private designs.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
POST "https://api.customerscanvashub.com/api/atoms/v1/designs/render-preview" \
-H  "accept: application/octet-stream" \
-H  "Content-Type: application/json" \
-H  "Authorization: Bearer <TOKEN>" \
-d '{"designId": "6698b073306ecea21de39a00", "ownerId": "crisford", "renderingConfig": {"width": 200, "height": 200, "fileFormat": "Jpeg"}}'
POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-preview
Content-Type: application/json
{
  "designId": "6698b073306ecea21de39a00",
  "ownerId": "crisford",
  "renderingConfig": {
    "width": 200,
    "height": 200,
    "fileFormat": "Jpeg"
  }
}
// Define request body
var body = """
{
    "designId": "6698b073306ecea21de39a00",
    "ownerId": "crisford",
    "renderingConfig": {
        "width": 200,
        "height": 200,
        "fileFormat": "Jpeg"
    }
}
""";

// Send request
var preview = await designAtomsServiceApiClient.RenderDesignPreviewAsync(body: body);
// Define request body
const body = {
    designId: "6698b073306ecea21de39a00",
    ownerId: "crisford",
    renderingConfig: {
        width: 200,
        height: 200,
        fileFormat: "Jpeg"
    }
};

// Send request
const preview = _designAtomsServiceApiClient.renderDesignPreview(null, null, body);
// Define request body
$data = [
    'designId' => '6698b073306ecea21de39a00',
    'ownerId' => 'crisford',
    'renderingConfig' => [
        'width' => 200,
        'height' => 200,
        'fileFormat' => 'Jpeg'
    ]
];

// Send request
$response = $designAtomsServiceApi->designAtomsServiceRenderDesignPreview(null, null, json_encode($data));

Preview with Variable Data

If you are working with templates that contain variable data (e.g., when implementing VDP scenarios), you may want to generate previews showing different values in those variable fields. You can get the list of variable fields at runtime by using the endpoint GET ​/api​/atoms​/v1​/designs​/{id}​/variables.

The following image shows an example of a populated template:

Design with stubs

To populate the text and logo, you need also provide a variableData array defining field values in the request body:

{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "variableData": [
    {
      "name": "Company Name",
      "type": "Text",
      "value": "CA Printing Inc."
    },
    {
      "name": "CompanyLogo",
      "type": "Image",
      "value": "https://example.com/logos/company.png"
    }
  ],
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}

Here, the name and type are case-sensitive strings. The type can be one of "Image", "ImagePlaceholder", "Text", or "InString". You can specify paths to images located in the following sources:

  • Public images uploaded to the Assets > Images:

    "public:folder-path/file-name.jpg"

  • User images upload to their private storage:

    "user:folder-path/file-name.jpg"

  • Images in an external system:

    "https://example.com/image.jpg"

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
POST "https://api.customerscanvashub.com/api/atoms/v1/designs/render-preview" \
-H  "accept: application/octet-stream" \
-H  "Content-Type: application/json" \
-H  "Authorization: Bearer <TOKEN>" \
-d '{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "variableData": [
    {
      "name": "Company Name",
      "type": "Text",
      "value": "CA Printing Inc."
    },
    {
      "name": "CompanyLogo",
      "type": "Image",
      "value": "https://example.com/logos/company.png"
    }
  ],
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}'
POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-preview
Content-Type: application/json
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "variableData": [
    {
      "name": "Company Name",
      "type": "Text",
      "value": "CA Printing Inc."
    },
    {
      "name": "CompanyLogo",
      "type": "Image",
      "value": "https://example.com/logos/company.png"
    }
  ],
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}
// Define request body as JSON string
var body = """
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "variableData": [
    {
      "name": "Company Name",
      "type": "Text",
      "value": "CA Printing Inc."
    },
    {
      "name": "CompanyLogo",
      "type": "Image",
      "value": "https://example.com/logos/company.png"
    }
  ],
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}
""";

// Send request
var preview = await designAtomsServiceApiClient.RenderDesignPreviewAsync(body: body);
// Define request body
const body = {
    designId: "6698b073306ecea21de39b11",
    ownerId: "crisford",
    variableData: [
        { name: "Company Name", type: "Text", value: "CA Printing Inc." },
        { name: "CompanyLogo", type: "Image", value: "https://example.com/logos/company.png" }
    ],
    renderingConfig: {
        surfaceIndex: 0,
        width: 300,
        height: 300,
        fileFormat: "Jpeg"
    }
};

// Send request
const preview = _designAtomsServiceApiClient.renderDesignPreview(null, null, body);
// Define request body
$data = [
    'designId' => '6698b073306ecea21de39b11',
    'ownerId' => 'crisford',
    'variableData' => [
        [
            'name' => 'Company Name',
            'type' => 'Text',
            'value' => 'CA Printing Inc.'
        ],
        [
            'name' => 'CompanyLogo',
            'type' => 'Image',
            'value' => 'https://example.com/logos/company.png'
        ]
    ],
    'renderingConfig' => [
        'surfaceIndex' => 0,
        'width' => 300,
        'height' => 300,
        'fileFormat' => 'Jpeg'
    ]
];

// Send request
$response = $designAtomsServiceApi->designAtomsServiceRenderDesignPreview(null, null, json_encode($data));

Image on Mockup

You may need to render a mockup, for example, attached to a variant of your PIM product.

Design with stubs

To do so, additionally pass the mockup ID.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
POST "https://api.customerscanvashub.com/api/atoms/v1/designs/render-preview" \
-H  "accept: application/octet-stream" \
-H  "Content-Type: application/json" \
-H  "Authorization: Bearer <TOKEN>" \
-d '{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "mockupId": "643d05f7c121a55df4598270",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}'
POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-preview
Content-Type: application/json
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "mockupId": "643d05f7c121a55df4598270",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}
// Define request body as JSON string
var body = """
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "mockupId": "643d05f7c121a55df4598270",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}
""";
// Send request
var preview = await designAtomsServiceApiClient.RenderDesignPreviewAsync(body: body);
// Define request body
const body = {
    designId: "6698b073306ecea21de39b11",
    ownerId: "crisford",
    mockupId: "643d05f7c121a55df4598270",
    renderingConfig: {
        width: 300,
        height: 300,
        fileFormat: "Jpeg"
    }
};
// Send request
const preview = _designAtomsServiceApiClient.renderDesignPreview(null, null, body);
// Define request body
$data = [
    'designId' => '6698b073306ecea21de39b11',
    'ownerId' => 'crisford',
    'mockupId' => '643d05f7c121a55df4598270',
    'renderingConfig' => [
        'width' => 300,
        'height' => 300,
        'fileFormat' => 'Jpeg'
    ]
];
// Send request
$response = $designAtomsServiceApi->designAtomsServiceRenderDesignPreview(null, null, json_encode($data));

A similar task is described in the article Rendering Preview Mockups.

To learn how to get the ID, let's consider two tasks.

Product Preview for a Selected Variant

If you are creating a preview for a specific selected product variation, you can request all design mockups for this variant through the GET ​/api​/storefront​/v1​/products​/{id}​/variant-mockups endpoint of the Storefront API. Then, get the mockup identifiers of the Preview type (the mockupType property) and call the endpoint POST /api/atoms/v1/designs/render-preview for each of them.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X GET \
  "https://api.customerscanvashub.com/api/storefront/v1/products/{id}/variant-mockups" \
  -H "accept: application/json" \
  -H "Authorization: Bearer <TOKEN>"

# Example response:
{
  "total": 2,
  "items": [
    {
      "mockupId": "643d05f7c121a55df4598270",
      "mockupName": "bc-preview-mockup-thumb",
      "mockupType": "Thumbnail",
      ...
    },
    {
      "mockupId": "643d05f7c121a55df4598271",
      "mockupName": "bc-preview-mockup-preview",
      "mockupType": "Preview",
      ...
    }
  ]
}

# After receiving the response, filter mockups of type "Preview"
# For example, using jq:
curl -s ... | jq '.[] | select(.mockupType == "Preview")'
GET https://api.customerscanvashub.com/api/storefront/v1/products/{productId}/variant-mockups
Authorization: Bearer <TOKEN>
Accept: application/json

// Example response:
{
  "total": 2,
  "items": [
    {
      "mockupId": "643d05f7c121a55df4598270",
      "mockupName": "bc-preview-mockup-thumb",
      "mockupType": "Thumbnail",
      ...
    },
    {
      "mockupId": "643d05f7c121a55df4598271",
      "mockupName": "bc-preview-mockup-preview",
      "mockupType": "Preview",
      ...
    }
  ]
}

// Filtering the result using JavaScript:
const mockups = [/* response data */];
const previewMockups = mockups.filter(mockup => mockup.mockupType === "Preview");
const previewMockupIds = previewMockups.map(mockup => mockup.mockupId);
console.log("Preview mockup IDs:", previewMockupIds);
// Request the list of mockups
var mockups = await storefrontApiClient.GetProductVariantMockupsAsync(productId, productVariantId);

// Filter mockups of type "Preview"
var previewMockups = mockups.Where(m => m.MockupType == "Preview").ToList();

// Output the result
foreach (var mockup in previewMockups)
{
    Console.WriteLine($"Mockup ID: {mockup.MockupId}");
}
// Request the list of mockups
const mockups = _storefrontApiClient.getProductVariantMockups({ productId: "{productId}", productVariantId: "{productVariantId}" });

// Filter mockups of type "Preview"
const previewMockups = mockups.filter(mockup => mockup.mockupType === "Preview");

// Output the result
previewMockups.forEach(mockup => {
    console.log(`Mockup ID: ${mockup.mockupId}`);
});
// Request the list of mockups
$mockups = $storefrontApiClient->productsGetProductVariantMockups("{productId}, {productVariantId}");

// Filter mockups of type "Preview"
$previewMockups = array_filter($mockups, function($mockup) {
    return $mockup['mockupType'] === 'Preview';
});

// Output the result
foreach ($previewMockups as $mockup) {
    echo "Mockup ID: " . $mockup['mockupId'] . "\n";
}

Preview of Other Products with This Design

You may want to upsell by selling accessories for your product (e.g., a business card holder for a business card) or offer to add another customizable product based on the same design. In this case, you need to add mockup IDs to the corresponding products and request a preview using them.

Note

If the aspect ratio for the placeholder on the design differs significantly from the aspect ratio of the design, the preview may not look entirely correct.

The choice of how to store mockup IDs with additional products is up to you. However, if you use the PIM approach, you can consider using tags and custom fields.

For example, let's consider the following setup:

  1. Configure a business card that has options, including size.
  2. Configure several business card holder models.

We can also set up business card holders in Customer's Canvas. We create a Size option in them with the same sizes as the business card. Prepare PSD mockups for each combination of business card holder and business card size, and attach them to the product.

Additionally, we can tag them, for example, with the tag Accessory and a custom field Parent Product with a value equal to the business card product ID.

To get product identifiers, you can search for products in your tenant by their tags and custom fields using the endpoint GET /api/storefront/v1/products. For example, let's get a list of products with the tag Accessory and a custom field Parent Product equal to 12345.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
  GET "https://api.customerscanvashub.com/api/storefront/v1/products?tag=Accessory&customFields=%7B%22Parent%20Product%22%3A%20%2212345%22%7D" \
  -H  "accept: text/plain" \
  -H  "Authorization: Bearer <TOKEN>"
GET https://api.customerscanvashub.com/api/storefront/v1/products?tag=Accessory&customFields={"Parent Product": "12345"}
var tag = "Accessory"; // Set the tag here
var customFields = new Dictionary<string, string> { { "Parent Product", "12345" } }; // Set custom fields here
var allProducts = await productsApiClient.GetAllProductsAsync(tag: tag, customFields: customFields);
var tag = "Accessory"; // Set the tag here
var customFields = { "Parent Product": "12345" }; // Set custom fields here
var allProducts = _productsApiClient.getAllProducts(null, null, null, null, null, tag, customFields);

API client not available

After you have found all such products, get their variant mockup IDs through GET ​/api​/storefront​/v1​/products​/{id}​/variant-mockups as described above, and generate images with our business card design through POST /api/atoms/v1/designs/render-preview.

Proof Images

The difference between a proof image and a preview is that the proof is as close as possible to the print file, except for the resolution and optional watermark. It will display elements that should only appear in the print, but not in the editor. For elements where the color in the print file differs from what the user sees in the editor, the color displayed will match the one in the print file.

To obtain it, use the following endpoints of the Design Atoms API:

  • Render the design proof image as binary data:

    POST /api/atoms/v1/designs/render-proof

  • Save the rendered proof image as a resource and retrieve its details:

    POST /api/atoms/v1/designs/render-proof/to-resource

Generating Proof Images

If you need to display such an image on the screen, generate it in JPEG format. For a multi-page design, it is necessary to call the endpoint multiple times with different surfaceIndex values. For example, for a three-page design:

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
# Loop through pages 0, 1, and 2
for index in 0 1 2; do
  response=$(curl -s -X POST "https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof" \
  -H "accept: application/octet-stream" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  -d '{
    "designId": "6698b073306ecea21de39b11",
    "ownerId": "crisford",
    "renderingConfig": {
      "surfaceIndex": '$index',
      "width": 300,
      "height": 300,
      "fileFormat": "Jpeg"
    }
  }')
  echo "Page $index: Proof generated"
done
# Example requests for pages 0, 1, and 2
POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof
Content-Type: application/json
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300, "height": 300,
    "fileFormat": "Jpeg"
  }
}

POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "surfaceIndex": 1,
    "width": 300, "height": 300,
    "fileFormat": "Jpeg"
  }
}

POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "surfaceIndex": 2,
    "width": 300, "height": 300,
    "fileFormat": "Jpeg"
  }
}
var designId = "6698b073306ecea21de39b11"; // Put your real design ID here
var ownerId = "crisford"; // Put your real owner ID here
var width = 300; // Put your real preview width here
var height = 300; // Put your real preview height here

// Loop through pages 0, 1, and 2
for (int index = 0; index < 3; index++)
{
    var body = $@"{{
        ""designId"": ""{designId}"",
        ""ownerId"": ""{ownerId}"",
        ""renderingConfig"": {{
            ""surfaceIndex"": {index},
            ""width"": {width},
            ""height"": {height},
            ""fileFormat"": ""Jpeg""
        }}
    }}";

    var response = await designAtomsServiceApiClient.RenderDesignProofAsync(body: body);
    Console.WriteLine($"Page {index}: Proof generated");
}
let designId = "6698b073306ecea21de39b11"; // Put your real design ID here
let ownerId = "crisford"; // Put your real owner ID here
let width = 300; // Put your real preview width here
let height = 300; // Put your real preview height here

// Loop through pages 0, 1, and 2
for (let index = 0; index < 3; index++) {
    let body = {
        designId: designId,
        ownerId: ownerId,
        renderingConfig: {
            surfaceIndex: index,
            width: width,
            height: height,
            fileFormat: "Jpeg"
        }
    };

    let response = await _designAtomsServiceApiClient.renderDesignProof(null, null, body);
    console.log(`Page ${index}: Proof generated`);
}
$designId = "6698b073306ecea21de39b11"; // Put your real design ID here
$ownerId = "crisford"; // Put your real owner ID here
$width = 300; // Put your real preview width here
$height = 300; // Put your real preview height here

// Loop through pages 0, 1, and 2
for ($index = 0; $index < 3; $index++) {
    $body = [
        'designId' => $designId,
        'ownerId' => $ownerId,
        'renderingConfig' => [
            'surfaceIndex' => $index,
            'width' => $width,
            'height' => $height,
            'fileFormat' => 'Jpeg'
        ]
    ];

    $response = $designAtomsServiceApiClient->designAtomsServiceRenderDesignProof(null, null, json_encode($body));
    echo "Page $index: Proof generated\n";
}

Proof with Variable Data

If you are working with a template containing variable data (e.g., when implementing VDP scenarios), you may want to see a proof with different values of variable fields. For this, pass the same variableData array in the request body as we used for rendering design previews.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
POST "https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof" \
-H  "accept: application/octet-stream" \
-H  "Content-Type: application/json" \
-H  "Authorization: Bearer <TOKEN>" \
-d '{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "variableData": [
    {
      "name": "Company Name",
      "type": "Text",
      "value": "CA Printing Inc."
    },
    {
      "name": "Logo",
      "type": "ImagePlaceholder",
      "value": "https://example.com/logos/company.png"
    }
  ],
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}'
POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof
Content-Type: application/json
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "variableData": [
    {
      "name": "Company Name",
      "type": "Text",
      "value": "CA Printing Inc."
    },
    {
      "name": "Logo",
      "type": "ImagePlaceholder",
      "value": "https://example.com/logos/company.png"
    }
  ],
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}
// Define request body as JSON string
var body = """
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "variableData": [
    {
      "name": "Company Name",
      "type": "Text",
      "value": "CA Printing Inc."
    },
    {
      "name": "Logo",
      "type": "ImagePlaceholder",
      "value": "https://example.com/logos/company.png"
    }
  ],
  "renderingConfig": {
    "surfaceIndex": 0,
    "width": 300,
    "height": 300,
    "fileFormat": "Jpeg"
  }
}
""";

// Send request
var proof = await designAtomsServiceApiClient.RenderDesignProofAsync(body: body);
// Define request body
const body = {
    designId: "6698b073306ecea21de39b11",
    ownerId: "crisford",
    variableData: [
        { name: "Company Name", type: "Text", value: "CA Printing Inc." },
        { name: "Logo", type: "ImagePlaceholder", value: "https://example.com/logos/company.png" }
    ],
    renderingConfig: {
        surfaceIndex: 0,
        width: 300,
        height: 300,
        fileFormat: "Jpeg"
    }
};

// Send request
const proof = _designAtomsServiceApiClient.renderDesignProof(null, null, body);
// Define request body
$data = [
    'designId' => '6698b073306ecea21de39b11',
    'ownerId' => 'crisford',
    'variableData' => [
        [
            'name' => 'Company Name',
            'type' => 'Text',
            'value' => 'CA Printing Inc.'
        ],
        [
            'name' => 'Logo',
            'type' => 'ImagePlaceholder',
            'value' => 'https://example.com/logos/company.png'
        ]
    ],
    'renderingConfig' => [
        'surfaceIndex' => 0,
        'width' => 300,
        'height' => 300,
        'fileFormat' => 'Jpeg'
    ]
];

// Send request
$response = $designAtomsServiceApi->designAtomsServiceRenderDesignProof(null, null, json_encode($data));

Obtaining PDF Proof

In addition to the JPEG format, it is also possible to generate PNG, TIFF, and PDF proof images.

Sometimes you need to provide the client with a PDF version. In this case, you may want to give them a low-resolution image. All elements are rasterized at 72 DPI resolution. Such a PDF will include all pages of the design.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
POST "https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof" \
-H  "accept: application/octet-stream" \
-H  "Content-Type: application/json" \
-H  "Authorization: Bearer <TOKEN>" \
-d '{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "fileFormat": "Pdf"
  }
}'
POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof
Content-Type: application/json
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "fileFormat": "Pdf"
  }
}
// Define request body as JSON string
var body = """
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "fileFormat": "Pdf"
  }
}
""";

// Send request
var proof = await designAtomsServiceApiClient.RenderDesignProofAsync(body: body);
// Define request body
const body = {
    designId: "6698b073306ecea21de39b11",
    ownerId: "crisford",
    renderingConfig: {
        width: 300,
        height: 300,
        fileFormat: "Pdf"
    }
};

// Send request
const proof = _designAtomsServiceApiClient.renderDesignProof(null, null, body);
// Define request body
$data = [
    'designId' => '6698b073306ecea21de39b11',
    'ownerId' => 'crisford',
    'renderingConfig' => [
        'width' => 300,
        'height' => 300,
        'fileFormat' => 'Pdf'
    ]
];

// Send request
$response = $designAtomsServiceApi->designAtomsServiceRenderDesignProof(null, null, json_encode($data));

Adding Watermark

You can add a watermark. This is always the word "Proof" and can be repeated several times or placed in the center of the design, and you can also control its opacity.

Watermark on proof images.

  • cURL
  • HTTP
  • C#
  • TS
  • PHP
curl -X \
POST "https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof" \
-H  "accept: application/octet-stream" \
-H  "Content-Type: application/json" \
-H  "Authorization: Bearer <TOKEN>" \
-d '{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "format": "Png",
    "watermarkEnabled": true,
    "watermarkOpacity": 0.7,
    "watermarkRepeat": true
  }
}'
POST https://api.customerscanvashub.com/api/atoms/v1/designs/render-proof
Content-Type: application/json
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "format": "Png",
    "watermarkEnabled": true,
    "watermarkOpacity": 0.7,
    "watermarkRepeat": true
  }
}
// Define request body as JSON string
var body = """
{
  "designId": "6698b073306ecea21de39b11",
  "ownerId": "crisford",
  "renderingConfig": {
    "width": 300,
    "height": 300,
    "format": "Png",
    "watermarkEnabled": true,
    "watermarkOpacity": 0.7,
    "watermarkRepeat": true
  }
}
""";

// Send request
var proof = await designAtomsServiceApiClient.RenderDesignProofAsync(body: body);
// Define request body
const body = {
    designId: "6698b073306ecea21de39b11",
    ownerId: "crisford",
    renderingConfig: {
        width: 300,
        height: 300,
        format: "Png",
        watermarkEnabled: true,
        watermarkOpacity: 0.7,
        watermarkRepeat: true
    }
};

// Send request
const proof = _designAtomsServiceApiClient.renderDesignProof(null, null, body);
// Define request body
$data = [
    'designId' => '6698b073306ecea21de39b11',
    'ownerId' => 'crisford',
    'renderingConfig' => [
        'width' => 300,
        'height' => 300,
        'format': 'Png',
        'watermarkEnabled': true,
        'watermarkOpacity': 0.7,
        'watermarkRepeat': true
    ]
];

// Send request
$response = $designAtomsServiceApi->designAtomsServiceRenderDesignProof(null, null, json_encode($data));

Performance Considerations

When generating previews, you may notice performance differences between the Asset Processor and Design Atoms endpoints. That is expected due to architectural differences. This section explains why these differences occur and how to optimize your workflow.

Key Differences Between Endpoints

Feature Asset Processor API Design Atoms API
Caching Yes (by namespace-name) No (returns a file)
Dynamic Mockups No Yes
Embedded Mockup Handling Uses embedded mockup Clears embedded mockup
Typical Use Case Static previews Dynamic previews (e.g., shopping cart)

Why Does Performance Vary?

  • Design Atoms may take longer because it:
    • Clears embedded mockups.
    • Applies external mockups on the fly.
    • Does not cache results by default.
  • Asset Processor is faster for repeated requests due to caching.

How to Choose the Right Endpoint

  • Use Asset Processor for:
    • Static previews where caching is acceptable.
    • Scenarios where embedded mockups are sufficient.
  • Use Design Atoms for:
    • Dynamic previews (e.g., shopping cart, real-time customization).
    • Cases requiring external mockups.

Optimization Tips

  • If using Design Atoms, consider:
    • Implementing client-side caching for repeated requests.
    • Pre-generating previews during low-traffic periods.
  • If using Asset Processor, ensure your use case does not require dynamic mockups.
Was this page helpful?
Thanks for your feedback!
Back to top Copyright © 2001–2025 Aurigma, Inc. All rights reserved.
Loading...
    Thank for your vote
    Your opinion is important to us. To provide details, send feedback.
    Send feedback