Manage PDF templates
The Product PDF plugin needs to be activated for your organization. You need to be given explicit permission to manage PDF templates.
Managed roles having this permission: Plugin admin.
Adding services to your template
View PDF templates
Select Product PDF from the main navigation bar. All existing templates available for your organization will be presented.
Note that if there are no specific templates created for your organization yet, only the system Default template is available:

Create a new PDF template
You can start creating a new template in two ways.
- a) Click the Plus icon in the upper right corner to start building your template from scratch:

b) Click the three-dots menu of an existing template and select Duplicate to start with the code from that template as basis:
- Fill in the template Name and select Set as my default if you have several templates and this template should be selected by default when generating PDFs.

- Insert/edit the HTML and CSS code in the Code panel.
Note:
As a standard, Bluestone PIM uses paged.js for generating PDFs from HTML. Please visit https://pagedjs.org/documentation/ for more information.To use another library, update the script tag with the path to your preferred library. Example:
<script src="path/to/your/pdf-library.js"></script>The library must be publicly accessible.
- You can use the preview function to verify the result of changes while editing the template. Select a relevant product by searching for a Product name or Product number in the upper right part of the page. A preview of the PDF is presented in the Preview panel:

- Optionally click Download the preview in the upper right part of the page to generate a PDF file for verification.
- Click Save when the new template is ready.
The new template is now listed in the PDF templates overview.
Note: The system Default template will be replaced by the first specific template created for your organization.
Edit a PDF template
From the PDF templates overview, click the three dots menu of the relevant template and select Edit to modify it.
If you have made changes to the code that you regret, click Revert change to go back to the latest saved version of the template. I.e. all changes performed after going into edit mode will be reverted.
Set a PDF template as default
From the PDF templates overview, click the three dots menu of the relevant template and select Set as default. Now this template will be selected by default when starting the process of generating a PDF.
Delete a PDF template
From the PDF templates overview, click the three dots menu of a PDF template and select Delete to remove it. Confirm the deletion by clicking Delete in the pop-up.
Note: The system Default template will be made available when the last organization-specific template is deleted.
Adding services to your template
Before you can use the data variables, you must enable the corresponding services in your template configuration. If a service is not enabled, the template will not be able to fetch the data.
How to enable services
You can update your template via the PATCH /templates/{id} endpoint and add the services you need in the "services" field as a comma-separated list.
Also see Bluestone PIM API Documentation - To update template
Example Configuration:
You need the ID of your template and then add this body content:
{
"services": "mediabank, pim-category, pim-attribute, pim-attribute-number, pim-relation"
}
Feature & Service Mapping
| Feature | Required Services | Optional Services | Variables |
| Product Core Data | N/A | $product | |
| Categories | pim-category | lookup-multi-category |
$categories |
| Attributes & Groups | pim-attribute | pim-attribute-number, pim-attribute-sorter | $groups |
| Media Assets | mediabank | mediabank-prefetch | $medias, $miscs.mediabaseurl |
| Variants | pim-variant | $variants | |
| Variant group | pim-variant-group | $variantGroup | |
| Relations | pim-relation | pim-label | $relations |
|
N/A |
$miscs |
||
| Freetext | cms-freetext | $freetexts | |
| Labels | pim-label | $labels |
Variables
$product
This is the main object that contains the core product information, including the basic details of the product being rendered.
Service required: N/A
Commonly used fields:
| key | description |
|---|---|
| $product.name | The full name of the product. |
| $product.number |
The product Number. |
| $product.id | The internal system ID. |
| $product.description |
The product description. |
| $product.archived | True/False |
| $product.type | Type of product (Single, Group, Bundle) |
| $product.state | State of product (CONNECTED, ARCHIVED ...) |
Example
<div class="product-header">
<h1>$product.name</h1>
<p>Item No: $product.number</p>
</div>
$categories
A list of all categories in the product’s path, organised from the root level down to the most specific level; to use it, you need to loop through it with #foreach.
Service required: pim-category
Service optional: lookup-multi-category
| Service | Detail |
|---|---|
| pim-category |
Fetch first category data returned from PIM |
| lookup-multi-category | Get all categories data of this product |
Commonly used fields:
| key | description |
|---|---|
| $category.name | The full name of the category. |
| $category.number |
The category number. |
| $category.id | The internal system ID. |
| $category.description | The category description. |
Example
This example supports multi-category
<!-- Existing categories processing -->
#if($categories && !$categories.isEmpty())
#set($crumb = "")
#set($idx = 1)
#foreach($cat in $categories)
#if($idx == $categories.size())
#set($crumb = "$crumb$cat.name")
#else
#set($crumb = "$crumb$cat.name > ")
#end
#set($idx = $idx + 1)
#end
<div><strong>Breadcrumb:</strong> $crumb</div>
<div class="attribute-list">
#foreach($cat in $categories)
<div class="attribute-item">
<h4>$cat.name</h4>
<div><strong>ID:</strong> $cat.id</div>
#if($cat.number)<div><strong>Number:</strong> $cat.number</div>#end
</div>
#end
</div>
#else
<div class="warning">No categories assigned</div>
#end
$groups
This variable contains all Attribute Groups and their associated attribute data.
Service required: pim-attribute
Service optional: pim-attribute-number, pim-attribute-sorter
Commonly used fields:
Group
| key | description |
|---|---|
| $group.name | The attribute group name |
| $group.definitions | Attribute list under group |
| $group.sorted | Attribute list under the group and sorting by priority |
Definition
| key | description |
|---|---|
| $definition.id | Attribute ID |
| $definition.name | Attribute name |
| $definition.number | Attribute number |
| $definition.value | Value |
| $definition.dataType | Type of attribute eg. integer, text |
Minimal Working Example:
#foreach($group in $groups)
<div class="attr-group">
<h3>$group.name</h3>
<ul>
#foreach($attr in $group.sorted)
#if($attr.value && $attr.value != "")
<li>
<strong>$attr.name:</strong> $attr.value
</li>
#end
#end
</ul>
</div>
#end
</div>
$medias
This object contains all assets from the Mediabank service. The data is automatically categorised into two main groups: Pictures and Documents.-
Pictures: A list of image files (e.g., JPG, PNG)
- Documents: A list of attached files (e.g., PDF Manuals)
Service required: mediabank
Service optional: mediabank-prefetch
| Service | Detail |
|---|---|
| mediabank | Pulls all assets (Images/Documents) associated with the product from the PIM. |
| mediabank-prefetch | fetch media from predefine config |
Setting up mediabank-prefetch
To display images and documents in your PDF that aren’t directly linked to a product, you need to define which assets the system should prefetch by using the following API call.
API Endpoint: POST /configs
Also see: Bluestone API doc - To create config
Filter assets by Labels or Filename Patterns.
Configuration Example
How it works:
"label;" Only fetch assets that have these exact label names in the PIM.
"contains;" Only fetch assets where the filename includes these specific strings.
Mediabank Structure ($medias)
When the system fetches media, it organises the data into three main keys, and understanding these keys allows you to target exactly what you want to display.
| Key | Contain |
|---|---|
| prefetch | Matches label; or contains; config. |
| all-pictures | Every picture file linked to the product. |
| all-documents | Every document/manual linked to the product. |
Commonly used fields:
| Key | Description |
|---|---|
| $media.urlImage | The full path of the asset |
| $media.labelName |
The label name of this asset |
| $media.assetResponse.name |
The asset name |
| $media.assetResponse.fileName |
The asset filename |
| $media.assetResponse.mediaInfo.contentType |
Content type eg. image/png |
| $media.assetResponse.mediaInfo.fileSize |
File size |
Example:
Rendering Pictures
To display all images, use $medias.pictures['all-pictures']
<h3>Product Images</h3>
<div class="image-grid">
#foreach($media in $medias.pictures['all-pictures'])
<img src="$miscs.mediabaseurl/$media.urlImage" alt="$media.assetResponse.name" style="width: 200px;" />
#end
</div>
</div>
Listing Documents
To list all downloadable files, use $medias.documents['all-documents']
<h3>Manuals & Certifications</h3>
<ul>
#foreach($media in $medias.documents['all-documents'])
<li>
<a href="$miscs.mediabaseurl/$media.urlImage">
$media.assetResponse.name
(Size: $media.assetResponse.mediaInfo.fileSize bytes)
</a>
</li>
#end
</ul>
</div>
$variants
This variable is available when the main product has the type GROUP. It contains a list of variants that belong to the group.
Service required: pim-variant
Commonly used fields: Same as $product
Example:
<div class="variants-container">
<h3>Product Variants</h3>
<table class="variant-table">
<thead>
<tr>
<th>Variant Name</th>
<th>SKU Number</th>
</tr>
</thead>
<tbody>
## Loop through each variant object
#foreach($variant in $variants)
<tr>
## Access data just like you do with $product
<td>$variant.product.name</td>
<td>$variant.product.number</td>
</tr>
#end
</tbody>
</table>
</div>
#end
$variantGroup
This variable is available when the main product is a Variant. It contains the Variant Group data, following the same structure as a Product.
Service required: pim-variant-group
Commonly used fields: Same as $product
Minimal Working Example:
<div class="variant-group">
<h4>Group name: $variantGroup.name</h4>
</div>
#end
$relations
This variable allows you to access other products linked to the main product. It is structured as a map where the key represents the relation type (e.g., have_optional_service_products, accessories), and the value is a list of product objects.
Service required: pim-relationCommonly used fields: Same as $product
Minimal Working Example:
<div class="relation-group">
<h4>Relation Type: $relation.key</h4>
#foreach($p in $relation.value)
<div>$p.name</div>
#end
</div>
#end
$miscs
Misc helper values
Service required: N/A
Commonly used fields:
| key | description |
|---|---|
| $miscs.mediabaseurl | The base url of asset. |
| $miscs.context |
A unique identifier (ID) that defines the specific scope of data to be retrieved from the PIM. |
| $miscs.locale | Specifies the language and regional settings |
| $miscs.title |
The title of PDF template file. |
Minimal Working Example:
<div class="attribute-list">
#foreach($k in $miscs.keySet())
#set($value = $miscs.get($k))
<div class="attribute-item">
<span class="property-name">$k</span>: #if($value)$value#{else}<em>null</em>#end
</div>
#end
</div>
#else
<div class="warning">No miscellaneous data found</div>
#end
$freetexts
This variable allows you to access all freetexts.
Service required: cms-freetext
Minimal Working Example:
<div class="attribute-list">
#foreach($entry in $freetexts.entrySet())
<div class="attribute-item">
<span class="property-name">$entry.key</span>
<div>$entry.value</div>
</div>
#end
</div>
#else
<div class="warning">No free texts found</div>
#end
$labels
Use this variable to access all labels associated with the product.
Service required: pim-label
Commonly used fields:
| key | description |
|---|---|
| $label.id | Label ID |
| $label.name |
Label name |
Minimal Working Example:
<div><strong>ID:</strong> $label.id</div>
<div class="warning">No labels assigned</div>
#end