---
title: "Generating proofs and previews for private designs | Customer&#x27;s Canvas Hub"
canonical: "https://customerscanvas.com/docs/hub/dev-center/scenarios/online-store/generating-proofs-and-previews/"
---

# Generating proofs and previews for private designs

> Comprehensive guide for developers on how to generate images for private design visualization using various API endpoints. It covers thumbnails, previews, and proof images, with detailed examples and explanations for each scenario.

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](#thumbnails)
- [Previews](#product-preview)
- [Proofs](#proof-images)

## 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](https://customerscanvas.com/docs/hub/dev-center/api/asset-processor.md):

-  Get the thumbnail file as binary data:

    [`GET /api/processor/v1/private-designs/{id}/preview/{namespace}/{name}/{width}x{height}`](https://customerscanvas.com/docs/hub/dev-center/api/asset-processor.md#tag/privatedesignprocessor/PreparePreview)

- Get a link to the thumbnail file:

    [`GET /api/processor/v1/private-designs/{id}/preview/{namespace}/{name}/{width}x{height}/url`](https://customerscanvas.com/docs/hub/dev-center/api/asset-processor.md#tag/privatedesignprocessor/PreparePreviewUrl)

:::note
Similar endpoints are available for other asset types, both [private](https://customerscanvas.com/docs/hub/dev-center/api/asset-processor.md#tag/privateimageprocessor) and [public](https://customerscanvas.com/docs/hub/dev-center/api/asset-processor.md#tag/imageprocessor) images, [fonts](https://customerscanvas.com/docs/hub/dev-center/api/asset-processor.md#tag/fontprocessor), and [public designs](https://customerscanvas.com/docs/hub/dev-center/api/asset-processor.md#tag/designprocessor).
:::

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**

```bash
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>"
```

**HTTP**

```bash
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/200x200
```

**C#**

```cs
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();
```

**TS**

```ts
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);
```

**PHP**

```php
$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`](https://customerscanvas.com/docs/hub/dev-center/api/asset-storage.md#tag/privatedesigns/GetAll) with filtering by owner name under which it was created (see the [Storefront User API](https://customerscanvas.com/docs/hub/dev-center/api/storefront.md#tag/storefrontusers)).

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

**cURL**

```bash
curl -X \
  GET "https://api.customerscanvashub.com/api/storage/v1/private-designs?ownerId=crisford" \
  -H  "accept: application/json" \
  -H  "Authorization: Bearer <TOKEN>"
```

**HTTP**

```
GET https://api.customerscanvashub.com/api/storage/v1/private-designs?ownerId=crisford
```

**C#**

```cs
var ownerId = "crisford"; // Put your user ID here
var designList = await privateDesignsApiClient.GetAllAsync(ownerId: ownerId);
```

**TS**

```ts
let ownerId = "crisford"; // Put your user ID here
let designList = await _privateDesignsApiClient.getAll(null, null, null, null, null, null, null, null, null, null, ownerId);
```

**PHP**

```php
$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**

```bash
# 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
```

**HTTP**

```
# 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
```

**C#**

```csharp
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}");
}
```

**TS**

```typescript
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}`);
}
```

**PHP**

```php
$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](images/show-stubs.png)

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

**cURL**

```bash
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>"
```

**HTTP**

```bash
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/300x200/url?stub=false
```

**C#**

```cs
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();
```

**TS**

```ts
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();
```

**PHP**

```php
$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**

```bash
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>"
```

**HTTP**

```bash
GET https://api.customerscanvashub.com/api/processor/v1/private-designs/6698b073306ecea21de39b11/preview/portal/thumbnail/160x120/url?force=true
```

**C#**

```cs
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();
```

**TS**

```ts
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();
```

**PHP**

```php
$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 (/dev-center/api//design-atoms>:

- Render the design preview as binary data:

    [`POST /api/atoms/v1/designs/render-preview`](https://customerscanvas.com/docs/hub/dev-center/api/design-atoms.md#tag/designatomsservice/RenderDesignPreview)

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

    [`POST /api/atoms/v1/designs/render-preview/to-resource`](https://customerscanvas.com/docs/hub/dev-center/api/design-atoms.md#tag/designatomsservice/RenderDesignPreviewToResource)

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](https://customerscanvas.com/docs/hub/dev-center/concepts/retention-policy.md) 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:

```json
{
  "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**

```bash
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"
  }
}'
```

**HTTP**

```http
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"
  }
}
```

**C#**

```cs
// 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);
```

**TS**

```ts
// 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);
```

**PHP**

```php
// 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`](https://customerscanvas.com/docs/hub/dev-center/api/design-atoms.md#tag/designatomsservice/GetVariables).

The following image shows an example of a populated template:

![Design with stubs](images/filled-stubs.png)

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

```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"
  }
}
```

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**

```bash
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"
  }
}'
```

**HTTP**

```http
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"
  }
}
```

**C#**

```cs
// 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);
```

**TS**

```ts
// 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);
```

**PHP**

```php
// 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](https://customerscanvas.com/docs/hub/dev-center/concepts/pim.md).

![Design with stubs](images/preview-mockup.png)

To do so, additionally pass the mockup ID.

**cURL**

```bash
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"
  }
}'
```

**HTTP**

```http
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"
  }
}
```

**C#**

```cs
// 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);
```

**TS**

```ts
// 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);
```

**PHP**

```php
// 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));
```

:::note
A similar task is described in the article [Rendering Preview Mockups](https://customerscanvas.com/docs/hub/dev-center/scenarios/product-images/rendering-preview-mockups.md).
:::

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`](https://customerscanvas.com/docs/hub/dev-center/api/storefront.md#tag/products/GetProductVariantMockups) 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`](https://customerscanvas.com/docs/hub/dev-center/api/design-atoms.md#tag/designatomsservice/RenderDesignPreview) for each of them.

**cURL**

```bash
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")'
```

**HTTP**

```http
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);
```

**C#**

```csharp
// 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}");
}
```

**TS**

```typescript
// 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}`);
});
```

**PHP**

```php
// 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](https://customerscanvas.com/docs/hub/designers-manual/mockups/pim-mockups.md) 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`](https://customerscanvas.com/docs/hub/dev-center/api/storefront.md#tag/products/GetAllProducts). For example, let's get a list of products with the tag **Accessory** and a custom field **Parent Product** equal to **12345**.

**cURL**

```bash
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>"
```

**HTTP**

```
GET https://api.customerscanvashub.com/api/storefront/v1/products?tag=Accessory&customFields={"Parent Product": "12345"}
```

**C#**

```cs
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);
```

**TS**

```ts
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);
```

**PHP**

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`](https://customerscanvas.com/docs/hub/dev-center/api/storefront.md#tag/products/GetProductVariantMockups) as [described above](#product-preview-for-a-selected-variant), and generate images with our business card design through [`POST /api/atoms/v1/designs/render-preview`](https://customerscanvas.com/docs/hub/dev-center/api/design-atoms.md#tag/designatomsservice/RenderDesignPreview).

## 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`](https://customerscanvas.com/docs/hub/dev-center/api/design-atoms.md#tag/designatomsservice/RenderDesignProof)

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

    [`POST /api/atoms/v1/designs/render-proof/to-resource`](https://customerscanvas.com/docs/hub/dev-center/api/design-atoms.md#tag/designatomsservice/RenderDesignProofToResource)

### 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**

```bash
# 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
```

**HTTP**

```
# 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"
  }
}
```

**C#**

```csharp
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");
}
```

**TS**

```typescript
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`);
}
```

**PHP**

```php
$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](#preview-with-variable-data).

**cURL**

```bash
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"
  }
}'
```

**HTTP**

```http
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"
  }
}
```

**C#**

```cs
// 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);
```

**TS**

```ts
// 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);
```

**PHP**

```php
// 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**

```bash
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"
  }
}'
```

**HTTP**

```http
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"
  }
}
```

**C#**

```cs
// 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);
```

**TS**

```ts
// 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);
```

**PHP**

```php
// 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.](images/proof-watermark.png)

**cURL**

```bash
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
  }
}'
```

**HTTP**

```http
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
  }
}
```

**C#**

```cs
// 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);
```

**TS**

```ts
// 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);
```

**PHP**

```php
// 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.
