Documentation
Create SDK integration
Using the Create SDK, Shutterstock can render the Create editor into an element you designate on your website, bringing many of the tools and capabilities of the editor to your customers. The SDK allows your application to interact with the editor to export in-progress and final designs, observe telemetry about the user’s behavior, and control parts of the editor experience. The SDK is divided into two parts:
- Integration - the web application that initializes the SDK and launches the Create editor in an iFrame that is rendered into a container or modal element in the host app.
- Provider - wired into the Create editor, it observes user actions, announcing events and sharing data out for consumption by partners. It also provides a handful of methods that allow the integration to safely dispose of the Editor. The SDK then uses window messaging in the browser to communicate between the two sides of the composite application. This document covers the responsibility of partners on the Integration side of the SDK.
Getting Started
Partners will include a reference to the SDK’s javascript library in their source code, which will provide an SDK object to interact with. Insert a script tag into your html that refers to the Create SDK javascript library using the following format:
<script src="https://www.shutterstock.com/create/integration.js"></script>
NOTE: The CORS settings on your site must allow scripts to be served and executed from the corresponding Shutterstock domains, in particular Content Security Policy (CSP) script-src directive.
Once you have added a reference to the CreateSDK javascript library, you can access the global SDK constructor from the window object.
Obtain Your Partner Consumer Key
Consumer keys, also known as apiKey or api key, are specific to each partner and integration. These keys allow Shutterstock to:
- identify the partner making the request
- validate that the integration has the necessary permissions to use the CreateSDK
- validate the host domain is associated with the key
How you obtain a key depends on the type of API account you have with Shutterstock.
Paid API Subscription Accounts
If you are a Shutterstock partner who has a paid API subscription, you should already be working with a partner manager on your account. Please contact your partner manager directly.
Free API Subscriptions
If you have a free API account from Shutterstock and want to try the Create SDK, please contact our sales team for help. If you have already created a Shutterstock API application, be sure to mention it. They may be able to add the Create SDK capability to your existing consumer key or help you set up a new application to experiment with.
Initialize Create SDK
By inserting the script tag, a CreateEditor method will be attached to the global window object. Use the CreateEditor method to initialize the SDK, which will confirm the requesting host matches one of the host values provided when you created your application.
Usage
const createSDK = window.CreateEditor(opts);
Parameters
Takes a plain JS Object of options for initializing the SDK.
Name | Type | Required | Description |
---|---|---|---|
apiKey | String | Yes | Partner consumer key to authenticate use of editor |
container | HTML Element | No | Element in which to embed the Editor. If not provided, this will default to showing the editor in a modal over top of your webpage. |
debug | Boolean | No | Truthy value will turn on console logging for enhanced info in the browser’s developer tools. |
Returns
CreateSDK object. The SDK has the following structure:
Name | Type | Description |
---|---|---|
closeEditor | Function | Allows the consuming application to remove the SSTK editor iFrame from the DOM, freeing up resources consumed. |
FileType | Object | Collection of file types that can be used to designate the type of file to export. |
exportDesign | Function | Exports a JSON file representing the design with unlicensed images. |
getDesign | Function | Function to get a preview of the document currently in the canvas. |
getUnlicensedAssets | Function | Returns an array of unlicensed assets used in the current design document, including the image IDs for use with the SSTK public API for licensing. |
hide | Function | Uses CSS to hide the SDK container and editor, although the SDK remains loaded in memory. Show method will re-display the editor. |
JpegQuality | Object | String bag with values guaranteed to work with the launchEditor option jpegQuality. |
launchEditor | Function | Function that opens a new or existing Create editor document |
name | String | “create-integration-library/integration” |
off | Function | Detaches event handlers that were previously listening. |
on | Function | Attaches handlers that listen for any supported event each time they are executed. |
once | Function | Attaches handlers that are executed once for any supported event, and then is automatically detached. |
replaceUnlicensedAssets | Function | Swaps images in the design for images URLs passed in, based on JavaScript matching asset IDs. Intended for replacing watermarked images with licensed images. |
setCustomUploadUrlAdjustment | Function | Configures the customUploadUrlAdjustment, which can also be set by a launch option. This provides prefix and suffix tokens to construct the path to custom uploads that you may have provided. This may become especially important when users have exported a design and are attempting to reload it after time has passed. |
setUploads | Function | Initializes the uploads controls’ asset manager section with images passed from the integrator. |
show | Function | If hidden using the hide method, show will re-display the editor container. |
Checkout the Codepen example to see a simple example of how mount and use the CreateEditor.
Browser and Language Support
The Create editor and SDK currently only support desktop users with modern browsers. The browser family and major version is used to determine when to allow the editor to launch and when to block launching.
Browser Matrix
The code below is used to check minimum browser versions and determine if Create should be launched or not.
export const supportedVersions = { [BrowserFamily.chrome]: '72', [BrowserFamily.firefox]: '65', [BrowserFamily.safari]: '11.0.3', [BrowserFamily.msedge]: '80', [BrowserFamily.opera]: '58', }; export const supportedAndroidVersion = '4.4'; export const supportedIOSVersion = '13';
Graphics Capabilities
Create requires users' devices to support WebGL in order to provide the real time interactivity with elements on the editor canvas. If users do not have adequately modern hardware or have disabled certain browser features, they may not be able to use WebGL. Learn more at get.webgl.com’s test page. In these cases, Create will present an error page and link to a self-help topic to address any configuration problems. Subscribe to the Error event to understand how often your users encounter this or other problems.
Mobile breakpoints
The Create SDK uses the browser and device type to determine if the user is on a mobile device. If an iOS or Android mobile device and browser are detected then Create will redirect users. The Create SDK does not use screen width or height to determine if the user is using a mobile device. If a specific viewport size needs to be defined, the recommendation from the Shutterstock UX team is to set the breakpoint to 800px. This will ensure an optimal user experience for the smallest possible screen size.
Tablets are recognized as separate devices from mobile and are able to launch the Create SDK.
Language and Translation
The editor UI and controls are translated into a variety of languages. Pass the language option when calling launchEditor to set the language for each user.
Editor-Supported Languages Codes
The editor supports a slightly smaller set of languages than the Shutterstock API does. See the language code supported by the API here.
Code | Language |
---|---|
cs | Czech |
da | Danish |
de | German |
el | Greek |
en | English |
es | Spanish |
fi | Finnish |
fr | French |
hi | Hindi |
hu | Hungarian |
id | Indonesian |
it | Italian |
ja | Japanese |
ko | Korean |
mr | Marathi |
nb | Norwegian |
nl | Dutch |
pl | Polish |
pt | Portuguese |
ro | Romanian |
ru | Russian |
sv | Swedish |
ta | Tamil |
te | Telugu |
th | Thai |
tr | Turkish |
vi | Vietnamese |
zh | Simplified Chinese |
zh | -Hant Traditional Chinese |
Release Methodology
There are two main areas of concern to ensure the ongoing working order of your integration with Create SDK:
- The library integration.js found in the SDK bootstrap code
- Changes to the core Create editor
SDK Library
Our primary strategy for integration.js is to always be additive to the SDK, rather than changing existing APIs. Your javascript integration code with the SDK should never need to change, except to take advantage of new features.
Create App
Create is adding features to the editor continually, and also is using the CI/CD release methodology to ship both bug fixes and new features regularly. Therefore, we will account for SDK scenarios in all changes going forward. We will be working with Shutterstock Partner Managers to provide information on configuring newly added premium opportunities as they become available.
SDK Reference
Methods
Initialization returns an SDK object that has many properties that allow partners to interact with and control the Create editor.
launchEditor(opts)
Launches the editor and either starts a new blank document, or provides an image or template ID from the Shutterstock library to use as a starting point in the editor canvas.
Usage
const options = {
ctaLabel: ‘Export’,
document: {
width: 600,
height: 300
},
onDesignComplete: (design)=>{
// code to interact with design object
}
};
await sdk.launchEditor(options);
// OR
sdk.launchEditor(options).then(()=> {
// your code here
})
Parameters
LaunchOptions Object - Required.
LaunchOptions
Name | Type | Required | Description |
---|---|---|---|
addStockContentMessage | String | No | Replaces the default message shown in a notification toast when stock content is added to a document in the editor with a custom message. |
assetUrl | String | No | URL to an image that can be used as a starting point for a new document |
bleedMargin | Number | No | Value in pixels of margin that is added to the outside of the core document dimensions to define a bleed area that might be cut by mechanical cutting in a print scenario. By default, the bleed margin is 0px.If the user activates the bleed line feature or the partner configures safetyMargin, a default of 38px per edge is added as a bleed area to the document dimensions. |
bypassUploadProcessing | String | No | When overrideUploadHandler is true, partners may bypass Create’s processing of upload files by designating the file type. Currently, only “pdf” file type has processing that can be bypassed, but the option is an array for forward compatibility. Use the FileType object as convenience to get the exact string the SDK is checking for. |
ctaLabel | String | No | Defines the label shown on the primary CTA button in the header that triggers the DesignComplete event. Default label is “Download” with a download icon. Icon hidden if ctaLabel is supplied. 30 char max. |
customUploadUrlAdjustment | Object | No | When providing your own asset management API, the customUploadUrlAdjustment dynamically provides the path and query string params to adjust any custom upload paths that you may have provided. This may become especially important when users have exported a design and are attempting to reload it after time has passed. See setCustomUploadUrlAdjustment for more information. |
document.width | String | No | Width of the document canvas when not opening a template or existing doc |
document.height | String | No | Height of the document canvas when not opening a template or existing doc |
document.lockCanvasDimensions | Boolean | No | Instructs the editor to 1) lock the canvas to the supplied document.width and document.height dimensions and 2) filter templates to show only those that match those dimensions. |
document.maxPages | Number | No | Maximum number of pages allowed in the editor for a single document. If undefined, defaults to 30 pages. |
enableUserAnalytics | Boolean | No | By default, user analytics are not collected. Set to true if you would like to receive UserInteraction events about the user’s behavior in the editor. |
features.templates.hidden | Boolean | No | Hides the templates button and panel from users so they cannot use Shutterstock templates. By default, templates are visible. When templates are hidden, the color selection tool will be opened by default when the editor is first launched. |
fileType | String | No | Define the file type for the exported design. Default is PNG. Can be a string or use a value from the exported FileType enum. |
fullStoryKey | String | No | Pass your organization’s FullStory key to track user actions inside of Create’s iFrame. The enableUserAnalytics option must also be set to true, indicating you have collected consent for user tracking from the current user. Tracking will only work if the fullStoryKey matches the key used in the FullStory script in the parent window. |
hideAddStockContentMessage | Boolean | No | Suppresses the notification toast shown when the user adds stock content to a document in the editor. |
hideCropMarks | Boolean | No | Instructs the editor to not add crop marks to the output of the document when exported via the call to action button. Crop marks add 37px to each edge of the output. |
hideStockContent | Boolean | No | Hides the side panel that allows users to search and browse through Shutterstock’s image library (and other stock content in the future). |
hideTopBar | Boolean | No | Hides the top horizontal bar of the Create editor. This bar normally contains a logo, the primary call to action button (complete/download) and a close button. Partners who hide the top bar with this flag will also need to implement their own buttons for the primary call to action and the close button that call the SDK methods completeDesignAndClose and closeEditor. |
imageId | Number | No | Optional image used as starting point for the document. Ignored if templateId is supplied. |
importAsset | DesignExport | No | An DesignExport object from a previously provided by the exportDesign method. |
jpegQuality | String | No | String value for low, medium, high or use the JpegQuality enum exported from the SDK. |
language | String | No | Pass a 2 character language code to control the language of the editor UI. Default is english. See supported languages here. |
onEditorOpen | Function | No | Callback/handler for EditorOpened event triggered when the editor app begins loading into the iFrame |
onEditorInteractive | Function | No | Function that runs when the EditorInteractive event is fired and the editor is fully loaded |
onDesignComplete | Function | No | Function that listens for the DesignComplete event when a user completes their design by clicking on the primary CTA |
onEditorClose | Function | No | Function that runs when the EditorClose event is fired |
onError | Function | No | Function that runs when the Error event is fired and receives an ErrorDetails object. |
overrideUploadHandler | Boolean | No | Instructs the editor to use asset management APIs provided by the host application to integrate a user’s saved images into the asset manager section of the Uploads control. |
preventEditorClose | Boolean | No | Prevent Editor from closing when the user clicks the close button |
safetyMargin | Number | No | Value in pixels that defines a ‘safe area’ inside of the document’s original dimensions that is guaranteed to be safe from cutting during the print process. By default this is 0. If set to a positive value, Create will automatically add a bleedMargin of 38px per edge to the document dimensions. |
templateId | String | No | Optional ID for a template to use as the starting point for the document. |
Returns
Promise<InitializedEditor>
You should await or .then()
the promise before interacting further with the SDK object. The launchEditor method will decorate the existing SDK object on which you called launchEditor with some internal SDK methods that you should not need.
addAssetToCanvas
Preconditions
You must set the overrideUploadHandler launch option to true in order to correctly use this method. Learn more about providing your own asset management API here.
Usage
See the usage section of the RequestAddAssetToCanvas event.
Parameters
Takes a single asset object.
Returns
Void.
closeEditor
Removes the iFrame that contains the Create editor and all resources from the DOM. Any design not exported or otherwise saved will be lost.
Usage
const closeOptions = {}; if (launchOptions.preventEditorClose) { closeOptions.forceEditorClose = true; } sdk.closeEditor(closeOptions);
Parameters
Name | Type | Required | Description |
---|---|---|---|
forceEditorClose | Boolean | No | Closes the editor even if the “preventEditorClose” flag is set. |
Returns
Void.
completeDesignAndClose
This method is used in connection with the launchEditor option hideTopBar, which suppresses the main navigation bar of the editor which includes the primary call to action button. Normally, the primary call to action button will fire both the DesignComplete and EditorClose events back-to-back. The completeDesignAndClose method empowers partners to mimic the primary call to action button from their own UI.
Usage
const myButtonClickHandler = () => {
sdk.completeDesignAndClose();
}
Returns
Void.
exportDesign
Exports the current elements and layout in the editor’s canvas. The returned object can later be imported using the importAsset option for launchEditor.
Preconditions
In order to keep the exported Design size down, it is strongly recommended that you set the overideUploadHandler launchEditor option to true, and provide your own asset management API. This will use URLs for uploads, keeping the export size down. Otherwise, user uploads will be serialized into the Design’s document.uploadedAssetsB64Blobs collection on export, which will swiftly increase the size of the object.
Usage
const currentDesign = await sdk.exportDesign();
Returns
Returns a Promise<DesignExport> to provide a complete representation of the design’s data that can be stored by partners and reopened later. If you call replaceUnlicensedAssets to swap licensed images into the editor before exportDesign, the asset URLs will be reverted to watermarked images during the export process. Note, tampering with the Design’s internal schema or data in any way may result in unexpected behavior.
DesignExport
A DesignExport represents all of the component parts of a design needed to restore it to the Editor’s canvas at a later time by passing it as a launchEditor option. This allows users to reopen a design in the editor and adjust it.
Name | Type | Required | Description |
---|---|---|---|
version | String | Yes | Provides forward compatibility if Shutterstock ever needs to update the schema of the export. |
document.height | Number | Yes | Height in pixels |
document.patches | String | Yes | Base64 representation of patches describing the separate elements of the design. |
document.title | String | Yes | Generated name of the design document. |
document.uploadedAssetsB64Blobs | Object | No | Key/value pairs of serialized images included in the design. Default value is undefined. |
document.width | Number | Yes | Width in pixels |
getDesign(opts)
This method allows the host application to get a current snapshot of the user’s canvas. It utilizes the same output settings configured when you called launchEditor on the fileType. If fileType === FileType.jpg, the SDK will also honor the value requested with the jpegQuality option.
Usage
const { getDesign, FileType} = sdk; const snapshot = await sdk.getDesign({ fileType: FileType.jpg, }); const url = URL.createObjectURL(snapshot.pages[currentPageIndex].imageBlob);
Parameters
Takes a single options object.
Name | Type | Required | Description |
---|---|---|---|
fileType | String | No | Define the file type for the exported design. Default is PNG. Can be a string or use a value from the exported FileType enum. |
jpegQuality | String | No | String value for low, medium, high or use the JpegQuality enum exported from the SDK. |
documentWatermarkOptions | DocumentWatermarkOptions | No | Defines a watermark that should be rendered on top of the design. |
DocumentWatermarkOptions
Use this format to define a watermark that can be placed on a design provide by the getDesign method.
Name | Type | Required | Description |
---|---|---|---|
url | String | Yes | Public URL of the image to use as the watermark. We suggest using a PNG with a transparent background. The URL must have CORS settings that allow it to be embedded/rendered into the Create editor. |
isTiled | Boolean | No | Indicates if the image should be tiled. Default is false. |
position | String | No | Predefined set of 9 position options: ‘topLeft’, ‘topCenter’, ‘topRight’, ‘middleLeft’, ‘middleCenter’, ‘middleRight’, ‘bottomLeft’, ‘bottomCenter’, ‘bottomRight’. Default position is “middleCenter” if isTiled == false, and position is ignored if isTiled == true. |
relativeSizePercentage | Number | No | The size of the watermark relative to the document. |
Returns
Promise<DesignSnapshot> - An object with information about the current state of the design. Any images with watermarks will still be watermarked. Learn more about the DesignSnapshot object.
getUnlicensedAssets
Reports out any assets that are used in the editor that need to be licensed. For images, this means they are watermarked.
Usage
const unlicensedAssets = await sdk.getUnlicensedAssets();
console.log(JSON.stringify(unlicensedAssets));
/*
[
{
"image_id": "506163361",
"asset_token": "as_9xHC4pXJrW"
},
{
"image_id": "506163362",
"asset_token": "ab_9xHC4pQJrN"
}
]
*/
Returns
Promise<AssetInfo> an array of AssetInfo objects that describe each of the unlicensed assets used.
AssetInfo
Name | Type | Required | Description |
---|---|---|---|
image_id | String | Yes | Sent as a string property, this is typically a numerical ID value. |
asset_token | String | Yes | Token needed by the SDK renderer service for swapping images out of the design. |
getUploadsInDocument
Reports out any uploads that are used in the current document. This method is useful if the Renderer service is used instead of a client side document render. It allows identifying any user uploaded assets in a design, and swap those assets’ sources for fresh URLs, that e.g. contain an authentication token, when they call the Renderer.
Usage
const uploadsInDocument = await sdk.getUploadsInDocument();
console.log(JSON.stringify(uploadsInDocument));
/*
[
{
"imageId": "image-id-0.9071800759653539",
"assetToken": "as_YXn7gaCsF"
}
]
*/
Returns
Promise<UploadInfo> an array of UploadInfo objects that describe each of the uploaded assets used.
UploadInfo
Name | Type | Required | Description |
---|---|---|---|
imageId | String | Yes | The id of the uploaded image |
assetToken | String | Yes | The assetToken of the uploaded image. This token must be used as the key in the assetReplacements array when calling the Renderer service. |
on, off, once
These three methods are responsible for programmatically adding or removing event handlers on supported events.
Parameters
Name | Type | Required | Description |
---|---|---|---|
EventName | String | Yes | The name of the event you are attempting to set or remove a handler for. This is NOT case sensitive. |
pointerToHandler | Function | Yes | Event handlers may be designated inline as a parameter or by passing a pointer to a function. |
Usage
const handlerForExampleEvent = () => {
// do some work
}
sdk.on(“ExampleEvent”, handlerForExampleEvent);
sdk.off(“ExampleEvent”, handlerForExampleEvent);
sdk.once(“EventName”, () => {
// do some work
});
Returns
Void.
hide, show
These methods are responsible for hiding and re-displaying the iFrame containing the Create editor using CSS. Hide leaves the SDK in memory so that the partner integration can continue interacting with the methods, however the end user will no longer be able to see and interact with the editor until show is called.
Parameters
None.
Usage
sdk.hide(); // editor iframe no longer displayed, sdk remains loaded sdk.show(); // editor iframe is re-diplayed
Returns
Void.
replaceUnlicensedAssets(opts)
Swaps watermarked images used in a design for licensed images obtained from the Shutterstock licensing API. Learn more about techniques to license assets here.
Usage
// first get the images you need to license const unlicensedAssets = await sdk.getUnlicensedAssets(); // call the Shutterstock licensing APIs // Partner backend provides auth token, subscriptionID on proxied call const licensedAssets = await yourApiProxy.licenseAssets(unlicensedAssets); // pass the results to replaceUnlicensedAssets await sdk.replaceUnlicensedAssets(licensedAssets.data);
Parameters
Takes an array of LicensedAsset objects. For ease of use, the partner can pass the data response from the Licensing API directly to replaceUnlicensedAssets without modification. Therefore, some properties are not required and the property names are formatted slightly differently than the rest of the SDK.
LicensedAsset
Name | Type | Required | Description |
---|---|---|---|
allotment_charge | Number | No | This data is part of the response from the licensing API and is not strictly required. |
download.url | String | Yes | The download Object has a single property, the url, which is a string. |
image_id | String | Yes | ID of the licensed image, necessary for the swap action to work. Should match the unlicensed image you are replacing |
license_id | String | No | This data is part of the response from the licensing API and is not strictly required. |
Returns
Promise that resolves as the images are swapped.
setCustomUploadUrlAdjustment(opts)
When a partner is providing their own asset management API to manage custom uploads, they provide URLs to user assets in the partner’s storage system. Partners may need to change the host and/or path to the asset storage over time. Partners may also use the query string on an asset URL to provide tokens or other security mechanisms that need to be refreshed regularly. Therefore, the SDK provides setCustomUploadUrlAdjustment to replace the entire host + path and query string of an asset URL, preserving only the final segment of the path to represent the asset’s id.
Example
const uploadedUrl = customUploadUrlAdjustment.prefix + originalAssetId + customUploadUrlAdjustment.suffix;
Preconditions
For setCustomUploadUrlAdjustment method to be necessary, the partner application must provide their own asset management API, as described here and support exportDesign where custom upload URLs would be stored.
Usage
// original asset URL // https://partner0.cloudstorage.net:1234/bucket/path/abcde?token=value const exportedDesign = yourAssetApi.getUserDesign('abcde'); const launchEditorOptions = { overrideUploadHandler: true, onRequestAddAssetToCanvas: addAssetHandler, onRequestDeleteAsset: deleteAssetHandler, onRequestUploadAsset: uploadAssetHandler, importAsset: exportedDesign, }; const sdk = await editorSdk.launchEditor(launchEditorOptions); // Call on a timer with fresh values as necessary while SDK is loaded sdk.setCustomUploadUrlAdjustment({ prefix: 'https://partner1.cloudstorage.net:5678/bucket2/path/', suffix: '?token=freshToken' }); // Final asset URL would be: // https://partner1.cloudstorage.net:5678/bucket2/path/abcde?token=freshToken
Parameters
Takes an options object.
Name | Type | Required | Description |
---|---|---|---|
prefix | String | Yes | Portion of the URL that should occur before the main ID/name of the original image. |
suffix | String | Yes | Portion of the URL that should appear after the query string. |
Returns
Void.
setUploads(assets)
This method sets the collection of image assets available in the uploads section of the editor. Asset urls point back to the partner’s asset manager. This should be called both when you first open the SDK, and also again from each of the asset upload handlers.
Preconditions
You must set the overrideUploadHandler launch option to true in order to correctly use this method. Learn more about providing your own asset management API here.
Usage
// get assets from partner storage
const userAssets = await yourAssetApi.getAllAssets();
// map assets from partner format to the SDK asset format
const mappedAssets = userAssets.map((asset) => {
return {...asset};
});
// launch the editor
await sdk.launchEditor({overrideUploadHandler: true});
// add the user uploads into the editor uploads panel
sdk.setUploads(mappedAssets);
See also the usage sections for the RequestUploadAsset and and RequestDeleteAsset.
Parameters
Takes an array of asset objects.
Returns
Void.
Objects
Common objects provided by the SDK or event handlers as parameters.
Asset
An object provided to the event handlers for asset management. This allows partners to manage assets in their private storage. Learn more about providing your own asset management API here.
Name | Type | Required | Description |
---|---|---|---|
height | Number | Yes | Height in pixels |
id | String | Yes | Unique ID for the asset |
label | String | Yes | A friendly label for the asset |
provider | String | No | This will always be ‘sdkUpload’. |
thumbnailHeight | Number | No | Thumbnail height in pixels |
thumbnailUrl | String | Yes | URL to a thumbnail shown in the Uploads section of the editor. |
thumbnailWidth | Number | No | Thumbnail width in pixels. |
url | String | Yes | URL to the full sized image for use when inserting an image to the canvas. |
width | Number | Yes | Width of the upload in pixels. |
DesignSnapshot
A DesignSnapshot object is provided as either the response to a call to getDesign or as a parameter passed to the event handler for DesignComplete.
Name | Type | Required | Description |
---|---|---|---|
currentPageIndex | Number | Yes | Index of the current page of the (multi-page) document |
height | Number | Yes | Height of the output |
id | String | No | The ID of the users design. If the user is anonymous we won’t have a design ID |
pages | DesignPage | Yes | For most output formats, this will contain one DesignPage for every page in the canvas. The exception to this behavior is PDF, when all pages of the canvas are output as a single DesignPage that is a multi-page PDF. |
title | String | Yes | The title of the user’s design |
width | Number | Yes | Width of the output |
The array from the DesignSnapshot contains an object for every page with its associated page properties. |
DesignPage
Object that describes individual pages within a design.
Name | Type | Required | Description |
---|---|---|---|
imageBlob | Blob | Yes | The blob for downloading the user design |
encodedArray | Uint8Array | Yes | Design encoded into an array |
fileSize | Number | Yes | Size of the output |
height | Number | Yes | Height of the output |
url | String | Yes | The url for displaying the user design |
width | Number | Yes | Width of the output |
FileType
This is found on the SDK object returned by the CreateEditor constructor. It contains the supported file formats the editor can export the design as. FileType essentially acts as an enum with the string representation of the file types. While you don’t strictly need to use the FileType object, it does guarantee the type you request maps correctly to the output types the SDK supports.
Usage
console.log(JSON.stringify(sdk.FileType))
/* output to console
{
png: 'png',
jpg: 'jpg',
pdf: 'pdf',
};
*/
const opts = {
fileType: sdk.FileType.png,
}
sdk.launchEditor(opts);
JpegQuality
When the fileType option for launchEditor is designated as FileType.jpg, you may also set the jpegQuality option. The JpegQuality object contains the 3 supported jpeg quality levels: low, medium, high.
While you don’t strictly need to use the JpegQuality object, it does guarantee your request maps correctly to the supported levels.
Usage
console.log(JSON.stringify(sdk.JpegQuality);
/* output to console
sdk.JpegQuality = {
low: 'low',
medium: 'medium',
high: 'high',
};
*/
const opts = {
jpegQuality: sdk.JpegQuality.high,
fileType: sdk.FileType.jpg,
}
sdk.launchEditor(opts);
Events
The CreateSDK uses events to communicate between the editor provider in the iframe and the web app integration that hosts it. Event handlers can be attached in two ways:
- As a property on the options object passed when the SDK is initialized using the “onEventName” format.
- Using either the on or once methods exposed on the SDK object and the “EventName” convention, i.e. sdk.on(“EventName”, () => { // your function here }). Event names are case-insensitive.
Handlers are bound for ONLY those events that are supported and shown in the table below.
Event Name | Option name | Fired when… |
---|---|---|
AddPageToDocument | onAddPageToDocument | User adds a new page to the document. |
DeletePageFromDocument | onDeletePageFromDocument | User deletes a page from the document. |
DesignComplete | onDesignComplete | User clicks the call-to-action button to export the design. |
EditorClose | onEditorClose | Immediately after the call-to-action OR if the user clicks the close button. |
EditorOpened | onEditorOpened | Editor react app begins bootstrapping itself in the container provided. |
EditorInteractive | onEditorInteractive | Editor is fully loaded and ready to use. |
UserInteraction | onUserInteraction | Echoes telemetry events collected by Shutterstock back out through the SDK to the partner. |
RequestAddAssetToCanvas | onRequestAddAssetToCanvas | User attempts to add an asset from the asset manager into the canvas. |
RequestDeleteAsset | onRequestDeleteAsset | User attempts to delete an asset that is shown in the asset manager section of the Uploads control. |
RequestUploadAsset | onRequestUploadAsset | User clicks the Uploads button in the Uploads sidebar control. |
StockContentUpdated | onStockContentUpdated | User adds or removes stock content such as images to the canvas. |
AddPageToDocument
This event is fired when the user adds or duplicates a page to the document.
Usage
const addPageToDocumentHandler = () => {
console.log(“User added a new page to the document“);
};
sdk.on(“AddPageToDocument”, addPageToDocumentHandler);
DeletePageFromDocument
This event is fired when the user removes a page from the document.
Usage
const deletePageFromDocumentHandler = ({ pageId: string }) => {
console.log(`User deleted a page from the document`);
};
sdk.on(“DeletePageFromDocument”, deletePageFromDocumentHandler);
Returns
Name | Type | Description |
---|---|---|
pageId | String | The unique identifier for the page that was deleted. |
EditorOpened
Announced when the Create Editor is launched and the Create editor React app begins bootstrapping itself within the container provided by the integrator.
Usage
const editorOpenedHandler = () => {
console.log(“Editor application has opened iframe);
};
sdk.on(“EditorOpened”, editorOpenedHandler);
EditorInteractive
Event is fired when the Create Editor is fully loaded and ready to use.
Usage
const editorInteractiveHandler = () => {
console.log(“Editor is interactive);
};
sdk.on(“EditorInteractive”, editorInteractiveHandler);
Error
The error event is fired when the Create Editor encounters significant or blocking error conditions. Partners are strongly encouraged to create a listener for the error event so you can log it to your systems and take remedial action as needed.
Usage
const errorHandler = (errorDetails) => {
const { id, message, data } = errorDetails;
console.error(`The user encountered the ${id} error with the message: ${message}`, data);
};
sdk.on(“Error”, errorHandler);
Parameters
There is a single object provided to the event handler as a parameter. It is an ErrorDetails object that is based largely on the ECMA Error object.
ErrorDetails
Name | Type | Required | Description |
---|---|---|---|
id | String | Yes | A string identifying the error type, if known. |
message | String | Yes | A human readable description of the error that occurred. |
data | Object | No | When available, more data describing the error will be contained in the data property of the Error object. |
Known Error Types
NOTE: This list will grow over time as more error conditions are identified.
ID | Message | Data |
---|---|---|
webgl-error | WebGL support not detected | { glVersion: string, platform: string, userAgent: string } |
Returns
Void.
DesignComplete
Optional handler that runs when a user completes a design as designated by clicking on the primary CTA in the header. This event is followed by an EditorClose event.
Usage
const designCompleteHandler = (designSnapshot) => {
const { pages, currentPageIndex } = designSnapshot;
console.log(`This design has ${pages.length} pages.`);
console.log(`The currently visible page in the canvas is page index ${currentPageIndex}`);
};
sdk.on(“DesignComplete”, designCompleteHandler);
Parameters
There is a single object provided to the event handler as a parameter. It is a DesignSnapshot object that describes information about the design that was just completed.
Returns
Void.
EditorClose
Optional function that runs when a user closes the editor
Usage
sdk.on(“EditorClose”, (opts) => {
const { userStatus } = opts;
console.log(`User was ${userStatus} when they closed the editor`);
}
Parameters
Options object with limited information about the SDK session that just completed when the editor closed.
Name | Type | Required | Description |
---|---|---|---|
userStatus | String | Yes | For SDK sessions, this should always be ‘anonymous’. |
Returns
Void
RequestAddAssetToCanvas
Occurs when a user attempts to add an image from the partner-provided uploads into the Canvas. This event handler is used in conjunction with the method addAssetToCanvas. Learn more about providing your own asset management API here.
Preconditions
- You must set the overrideUploadHandler launch option to true in order for this event to be fired.
- The host domain of the images you are attempting to insert into the canvas must have the access-control-allow-origin set to permit shutterstock domains. Typically this can be achieved by providing just a “*” wildcard. Learn more about the special CORS restrictions on Canvas elements.
Usage
sdk.on(“RequestAddAssetToCanvas”, (addAsset) => {
// Optional provide notification to user
if(!window.confirm("Add image to canvas?")) return;
// pass the addAsset event object back to the SDK
sdk.addAssetToCanvas(addAsset);
}
Parameters
Event handler receives an AddAsset event object.
AddAsset
Name | Type | Required | Description |
---|---|---|---|
item | Asset | Yes | The asset being added. |
position | Float32Array | No | Optionally provided by canvas when an asset is drag and dropped from the uploads panel to the canvas. Position 0 is the X coordinate and position 1 is the Y coordinate. |
type | String | Yes | Value used by the canvas to determine what type of Node to add. |
RequestDeleteAsset
Occurs when a user attempts to delete an uploaded image from the uploads section of the editor. This event handler is used in conjunction with the method setUploads. Learn more about providing your own asset management API here.
Preconditions
You must set the overrideUploadHandler launch option to true in order for this event to be fired.
Usage
sdk.on(“RequestDeleteAsset”, (deleteAsset) => {
const { item } = deleteAsset;
if(!window.confirm("Delete image from asset manager?")) return;
// call your API to delete the item
await yourAssetApi.delete(item.id);
// update the uploads to the current full set of assets
const partnerAssets = await yourAssetApi.getAllAssets();
const assetsInSDKFormat = partnerAssets.map((parnterAsset) => {
// do any necessary mapping
return { ...parnterAsset }
});
// refresh the list of uploads in the SDK
sdk.setUploads(assetsInSDKFormat);
}
Parameters
Event handler receives an DeleteAsset event object, which can be inspected and transformed as needed before deleting from partner storage.
DeleteAsset
Name | Type | Required | Description |
---|---|---|---|
item | Asset | Yes | The asset being deleted. |
RequestUploadAsset
Occurs when a user attempts to upload an image using the upload button on the Uploads panel of the editor. The primary use case of this event is to be used in combination with the overrideUploadHandler launchEditor option where the partner provides their own asset management API. Learn more here. However, partners can listen to this event regardless of whether they are providing their own asset management API. When providing an asset management API, the partner should use the handler to intercept the upload event. This gives the partner the opportunity to generate a unique ID and URL for the asset and store it in their system. Before the handler is done executing, it must call the partner’s asset management API to retrieve the asset list from the partner system that contains the new upload and pass it to the SDK by the setUploads method.
Usage
sdk.on(“RequestUploadedAsset”, (uploadedAsset) => {
// first, store the upload to the partner system
// responsible for generating a unique ID & URL to asset
yourAssetApi.post(uploadedAsset);
// retrieve the current full set of assets
const partnerAssets = await yourAssetApi.getAllAssets();
// map from partner storage format to SDK asset format
const assetsInSDKFormat = partnerAssets.map((parnterAsset) => {
return { ...parnterAsset }
});
// refresh the list of uploads in the SDK
sdk.setUploads(assetsInSDKFormat);
}
Parameters
Event handler receives an UploadAsset event object, which can be inspected, transformed and added to partner storage before being passed along to setUploads to add it to the Editor’s uploads panel.
UploadAsset
Name | Type | Required | Description |
---|---|---|---|
file | File | Yes | Standard File object describing the file the user wants to upload. |
height | Number | Yes | Height of the upload in pixels. |
thumbUrl | String | Yes | URL for a thumbnail of the uploaded image. |
width | Number | Yes | Width of the upload in pixels. |
StockContentUpdated
Fired when a user alters the stock images that are used in the canvas, including both singular and multi-select add and remove operations. The event data gives you an accounting of the changes and the resulting state in the canvas, allowing the partner to accurately account for the licenses needed in the design.
Usage
sdk.on(“StockContentUpdated”, (event) => {
const { added, inUse, removed, updateType } = event;
console.log(`Content was ${updateType}.`, added.length ? added : removed);
console.log(`Count of stock in the canvas: ${inUse.length}`);
}
Parameters
Event handler receives a single data parameter.
Name | Type | Required | Description |
---|---|---|---|
added | string | Yes | Array of image IDs that were added to the canvas. |
inUse | string | Yes | Array of all image IDs that remain in the canvas after the operation |
removed | string | Yes | Array of image IDs that were removed from the canvas. |
updateType | string | Yes | ‘add’, ‘delete’, ‘replace’, ‘initialize’ |
UserInteraction
Provides telemetry about the user’s behavior inside of the editor on a wide array of events that they may trigger. Some interactions have a data element with metadata about the user’s action that will tell you more.
Preconditions
Shutterstock will neither collect user telemetry, nor send it back to you on this UserInteraction event, unless enableUserInteraction is set to true in options passed to launchEditor. This relies on you to get consent from your users and pass that response to the Create SDK before any non-critical user data is recorded.
Usage
sdk.on(“UserInteraction”, (event) => {
const { eventName } = event;
console.log(`The user just triggered a ${eventName} in the editor`);
}
Parameters
Event handler receives a single event parameter.
Name | Type | Required | Description |
---|---|---|---|
eventName | String | Yes | Friendly name of the event. See EventTypes table. |
resultsMatched | Boolean | No | Indicates whether search results were returned on a search event. |
searchTerm | String | No | The term that was used to conduct a search event. |
UserInteraction Event Types
Supported events from the SDK.
- indicates events that have the resultsMatched and searchTerm properties on the event object. ** indicates events that may not occur due to UI elements that are hidden or suppressed.
eventName | Description |
---|---|
Design Complete CTA2 | Primary call to action button clicked |
Images Tab | Images tab clicked |
Upload clicked | Upload Click |
Upload Error | Upload Error |
Upload success | Upload success when images load into the tray |
Images Tab | Stock Photos tab clicked |
Catalog Tab2 | Catalog tab clicked |
Uploads Tab | Uploads tab clicked |
Images Search2 | Images search conducted |
Text Tab | Text tab clicked |
Shapes Tab | Shapes tab clicked |
Shapes Search1 | Shapes search conducted |
Graphics Tab | Graphics tab clicked |
Graphics Search1 | Graphics search conducted |
Drawing Tab | Drawing tab clicked |
Predict Tab2 | Predict tab clicked |
Smart Resize Tab | Smart resize tab clicked |
Canvas Tab | Canvas tab clicked |
Templates Tab | Templates tab clicked |
Templates Search1 | Templates search conducted |
Collage Tab | Collage tab clicked |
Change Canvas Color | Change canvas color clicked |
Add BG image - Computer | Add background image from computer clicked |
Add BG image - Stock Photos | Add background image from stock photos clicked |
BG Image Search2 | Background image search conducted |
Textures | Textures control clicked |
Effects | Effects control clicked |
Rotate Layer | Rotate layer control clicked |
Flip Layer Vertically | Flip vertically clicked |
Flip Layer Horizontally | Flip horizontally clicked |
Delete Layer | Delete layer clicked |
Settings | Settings gear clicked |
Align & Snap - Enabled | Align & Snap feature enabled |
Align & Snap - Disabled | Align & Snap feature disabled |
Show bleed marks - Enabled | Show bleed marks enabled |
Show bleed marks - Disabled | Show bleed marks disabled |
Show grid on canvas - Enabled | Show grid on canvas enabled |
Show grid on canvas - Disabled | Show grid on canvas disabled |
Undo | Undo clicked |
Redo | Redo clicked |
Layers | Layers control clicked |
Returns
Void.
Common Scenarios
Licensing Workflow
Your application can license assets without CreateSDK by making web requests to the Shutterstock API. You may do that with an API client such as the Shutterstock API JavaScript SDK and an API token to talk to the Shutterstock public API endpoints. Learn more about API clients here. Learn more about how to obtain a token here. Most partners use the “Getting tokens from your account page” option, and store the token securely When a partner combines the Shutterstock API with the CreateSDK, your application can create an end-to-end licensing experience in several ways:
- Before launching the editor
- While the user is editing the design in the canvas
- When the user clicks the call to action button to complete the design
- After the SDK has been unloaded from the browser, or by a backend system
Pre-Launch Licensing Using LaunchEditor Method
This flow requires you to license the Shutterstock image up front before launching the Create SDK, enabling you to open the editor with that licensed image as the starting point.
- Use one of the Shutterstock API clients to build your own search UX to help your user find a single image they want to use.
- Once the user selects an image they want, use your application’s purchase flow to trigger the API client to license the image.
- Configure the clean URL provided by the API as the assetUrl property of launchEditor’s opts object.
- To prevent the user from adding unlicensed content after the editor opens, you must also set hideStockContent property to true.
- When the Create Editor is launched, the user will see the unwatermarked image in the editor as they build their design.
- All entry points to search for unlicensed stock content will be hidden from your application’s users.
- Users can complete & download the final design with the licensed image included using either getDesign method or by listening to the DesignComplete event. IMPORTANT: Because the image URLs you obtain from the licensing API expire after several hours, this flow is not recommended for partners who intend to support saving a draft design for later editing using the exportDesign method. An alternate approach is to use the imageId launchEditor option instead of the assetUrl option, which will load the watermarked image. Then use getUnlicensedAssets and replaceUnlicensedAssets to replace the watermarked images, similar to other licensing flows.
License While Designing Using the StockContentUpdated Event
When a StockContentUpdated event fires, the partner receives data from the editor that allows them to update prices and shopping carts. Optionally, partners can license immediately while still using the editor. This may be desirable if users want an opportunity to see their design with unwatermarked assets before finalizing the design.
- Partner provides StockContentUpdated event handler (launch option or on method)
- Event provides data including the list of in-use assets with an updateType (add or remove) with arrays of the image Ids of those assets that were impacted. a. NOTE: The partner can call getUnlicensedAssets at any time to confirm the full list of in-use assets.
- Partner optionally can offer to let the user see the design without watermarks by licensing immediately a. If user agrees, partner can show their purchase flow in a modal, or leverage the hide/show methods of the SDK b. Partner calls the Shutterstock API to get the clean licensed assets c. Partner calls replaceUnlicensedAssets to update the images in the editor d. SDK swaps watermarked images for the asset URLs passed in e. If the user does NOT license the images, they can continue to edit the design with watermarked images
- After licensing, you can repeat the steps if additional licensed images are added
- The DesignComplete event gives you one last chance to check for assets that need to be licensed or provide a final purchase UX. See the License After DesignComplete Event flow. NOTE: If the user adds the same image they licensed to the canvas again, it will again start off with a watermark. The host application is responsible for inspecting newly inserted images and providing the licensed image URL if it finds that it matches an image the user already licensed in the same SDK session.
License After DesignComplete Event
The user will work with watermarked images up until the time they are ready to complete their design and hopefully make a license purchase. The SDK lets you pause the DesignComplete event, license images, swap out watermarked images and extract a final piece of artwork with no watermarks. Once done, the partner can force the editor closed manually.
- Set launchEditor option preventEditorClose to true and launch the create editor using a blank canvas as the starting point.
- The user searches for images in the images panel on the left side, and can insert watermarked images into their design.
- Partner configures a handler for the DesignComplete event
- When the user clicks the primary call to action button, the SDK triggers your DesignComplete event handler, which needs to do several things: a. Calls getUnlicensedAssets to get the list of assets that need licensing. b. If the design does not have unlicensed assets, call closeEditor with forceEditorClose option set to true to complete the flow, otherwise continue. c. Depending on your shopping cart/licensing UX, you may also want to call the SDK hide() method. If you need to re-render the editor, you can click show().
- Call the Shutterstock licensing API with the details of each unlicensed asset and collect the response data, including urls to the clean, unwatermarked images.
- Call replaceUnlicensedAssets, passing the assets (gatekeeperUrls in the old SDK), including IDs and URLs, back into the CreateSDK method.
- Call getDesign to get a new download of the design without watermarks.
- Call closeEditor to dispose of the SDK from the browser, passing the forceEditorClose option as true.
- Print, save or download the final artwork for the user in your application. NOTE: If you also export the design JSON for later editing, the licensed images will be reverted and will need to be licensed again.
License After SDK Unload
In cases where a partner’s design editing experience is completely disconnected from the partner systems that do licensing with Shutterstock’s licensing API, it may be necessary to re-render the design with the newly licensed content. There are two options to swap licensed images into a design after you have unloaded the Create SDK & editor.
Reload the Editor
The simplest solution is to reload the Create SDK editor in the browser:
- First, export the design as a JSON file & the list of images that need to be licensed
- Unload SDK to completely
- Proceed through partner workflow
- Directly use Shutterstock’s licensing API to license the images and save the information
- Reload the Create SDK into a hidden container element, designating the JSON file as a the launchEditor option importAsset
- Provide an array of imageIds and asset URLs for the licensed images to the SDK method replaceUnlicensedAssets
- Call getDesign to get the finalized design
- Call closeEditor to remove the Create SDK from the browser
Rendering Service
In some scenarios, it may not be possible to reload the editor into a browser. For example, you may have a disconnected backend system responsible for managing checkout services. Instead of reloading Create SDK in a browser, the system can use the licensing API and Create SDK’s companion GraphQL Rendering Service to get the final rendered design.
- User goes through design experience with the Create SDK.
- When ready to complete the design, instead, the partner should gather 3 pieces of necessary information: a. Call exportDesign to obtain the DesignExport. b. Call getUnlicensedAssets to get the list of images that need to be swapped out for licensed images. c. If you have configured your own asset management system for handling user uploads by setting the LaunchOption overrideUploadHandler to true, you may also need to call getUploadsInDocument to get the list of uploaded assets in the design. Learn more here.
- Proceed through partner workflow, shopping cart, payment experience, etc.
- Partner backend system calls Shutterstock’s licensing API to license the necessary images.
- Partner backend system calls Create Rendering Service’s GraphQL endpoint, passing the DesignExport, the array of images & URLs to swap into the design, along with necessary information to identify the partner such as the apiKey (consumer key), referer and the API token.
- Renderer Service will return a renderId in response.
- Partner backend system will poll the renderer finishing service, passing the renderId and other necessary information to identify the partner.
- Renderer Service will return the status of the render job and/or URL(s) to the final rendered output. See the documentation for the Renderer Service for full details.
User Identity and Asset Management
User Session
The Create SDK version of Shutterstock’s editor maintains the state of the user’s design during the current session only. The user has a temporary session with the editor, but is not required to create a user account with Shutterstock. This is known as an ‘anonymous’ session. When the editor and SDK are closed, the design is not preserved automatically. This means the user’s identity is always tied back to their identity in the partner system. This separation of concerns allows partners to control their users’ data at every stage of the workflow and leverage the SDK for editing and rendering their designs. Partners are provided a set of tools to complete important use cases:
- Enabling user analytics collection with user consent.
- Saving in-progress designs so users can complete them later.
- Managing uploaded assets such as logos or profile pictures that a user may need repeatedly on different designs.
Analytics and Privacy
It is important to honor the user’s privacy choices regarding collecting user telemetry and analytics. To ensure maximum safety and compliance, by default, Shutterstock will not collect or transmit any user telemetry back to the partners. If a user has consented to collecting analytics, the partner must set the launchEditor option enableUserAnalytics to true in order for Shutterstock to record user telemetry and echo it back via the UserInteraction event.
Saving and Restoring Designs
Because the user’s session is temporary, if the partner application intends to support saving and reopening a design later to continue editing, they must provide storage for a DesignExport object. Typically a partner may offer a “Save For Later” type button in their application, which triggers the exportDesign SDK method. This returns the object that can be stored in the partner system relative to the currently logged in user. When that user later returns, the partner can allow them to browse any saved projects. If a user decides to open an existing design project, the partner must set the stored ExportDesign object into the importAsset property of the launch Editor options object. NOTE: If the partner is NOT overriding the upload handlers with their own storage API to manage user-uploaded assets, the SDK will serialize those assets directly into the DesignExport object. This will swiftly increase the size of the DesignExport object. Therefore, it’s strongly recommended that if partners who want to support saving and restoring designs, that they also override the asset upload handlers so you can store the assets in your own system, providing URLs to the assets.
Managing Custom Asset Uploads
By default, the SDK version of the Create Editor does not support storing a collection of custom, user-uploaded assets that is persistent between sessions. Users do have the ability to make uploads one by one, which are inserted directly into the canvas. While convenient, as noted elsewhere, that will greatly increase the size of an exported design. So if you intend to support saving and restoring in-progress designs, it is strongly recommended that you provide your own storage for custom uploads.
Allowed Content Types
The Create Editor, and therefore the SDK, support uploads with the following content type, also known as mime type.
Type | Upload File Extensions | Mime | RequestUploadAsset Converted To |
---|---|---|---|
JPEG Images | .jpeg, .jpg | image/jpeg | Not applicable |
Portable Network Graphics | .png | image/png | Not applicable |
Scalable Vector Graphics | .svg | image/svg+xml | Not applicable |
Adobe Portable Document Format | application/pdf | Up to 3 PNG images*** |
*** Multipage PDFs can be split into a maximum of 3 pages that are converted to PNG images. Each image will fire a separate RequestUploadAsset event.
Partner Asset Manager APIs
At minimum, a partner asset API must support these commands:
- Retrieve a list of available assets for the current user
- Upload a new asset to the user’s collection
- Delete a previously uploaded asset
Configuring Asset Management
To configure your instance of Editor to accept and use uploaded images, you must provide event handler functions for these Editor SDK events:
- RequestUploadAsset: Happens when the user uploads an image
- RequestAddAssetToCanvas: Happens when the user clicks an uploaded image in the panel
- RequestDeleteAsset: Happens when the user deletes an uploaded image
// launch the editor with the upload handler overridden
await sdk.launchEditor({overrideUploadHandler: true});
// retrieve existing uploads from partner API
const userAssets = await yourAssetApi.getAllAssets();
// map assets from partner format to the SDK asset format
const mappedAssets = userAssets.map((asset) => {
// do any necessary mapping
return { ...asset }
});
// initialize assets when editor is first launched
sdk.setUploads(mappedAssets);
// implement upload handler
sdk.on(“RequestUploadedAsset”, (uploadedAsset) => {
// first, store the upload to the partner system
// responsible for generating a unique ID & URL to asset
yourAssetApi.post(uploadedAsset);
// retrieve the current full set of assets
const partnerAssets = await yourAssetApi.getAllAssets();
// map from partner storage format to SDK asset format
const assetsInSDKFormat = partnerAssets.map((parnterAsset) => {
return { ...parnterAsset }
});
// refresh the list of uploads in the SDK
sdk.setUploads(assetsInSDKFormat);
}
// implement an add-to-canvas handler
sdk.on(“RequestAddAssetToCanvas”, (addAsset) => {
// Optional provide notification to user
if(!window.confirm("Add image to canvas?")) return;
// pass the addAsset event object back to the SDK
sdk.addAssetToCanvas(addAsset);
}
// implement an delete handler
sdk.on(“RequestDeleteAsset”, (deleteAsset) => {
const { item } = deleteAsset;
if(!window.confirm("Delete image from asset manager?")) return;
// call your API to delete the item
await yourAssetApi.delete(item.id);
// update the uploads to the current full set of assets
const partnerAssets = await yourAssetApi.getAllAssets();
const assetsInSDKFormat = partnerAssets.map((parnterAsset) => {
// do any necessary mapping
return { ...parnterAsset }
});
// refresh the list of uploads in the SDK
sdk.setUploads(assetsInSDKFormat);
}
Providing Your Own Nav Bar
Partners can use the hideTopBar property on the launchEditor options parameter to hide the primary nav bar that appears across the top of the editor. However, if you do this, you must also replace the functionality provided by the default nav bar:
- Primary call to action button - This is the button that triggers the DesignComplete event when the user clicks it, immediately followed by the EditorClose event. You can use the completeDesignAndClose method to replicate this behavior with your own UI element.
- Close button - This X button simply closes the editor and abandons any designs that are in progress in
the canvas. Use the closeEditor method with your own UI element to replace it.
- If you set the preventEditorClose to true on the launchEditor options, you will need to set the forceEditorClosed option to true when you call closeEditor.
- Shutterstock brand logo - The default nav bar will show a small Shutterstock logo. If you replace the default nav bar, you will need to render a copy of the logo, which can be found here and dimensions should be 36 x 36 pixels. After downloading the zip file in the link use the ‘red viewfinder symbol logo’.
- If you are not providing a container element in the LaunchOptions, the editor will appear inside Create’s default modal. There is not a way to inject your custom nav bar into the modal. So, instead of using Create’s out-of-the-box modal control, use your own modal behavior/control that contains 2 elements:
- your custom nav bar and 2) a container element provided to launchEditor where the editor can be displayed side by side with your nav bar.
Printing Safety
The Create editor has several LaunchOptions that can help users anticipate the limitations of taking their design from the digital world to the print world. Use these options to help your users’ designs look good when they go through the printing and cutting process.
Safety Line
If either bleedMargin or safetyMargin options are configured, Create will render a dashed safety line on the canvas indicating where elements risk being cut during the printing process. If not configured, the user can manually turn on the safety line on the settings menu under the Editor canvas. By default this adds a bleed margin of 38px to the dimensions of the document, and renders the safety line on the original document dimensions.
bleedMargin | safetyMargin | Bleed line in canvas options | Dashed safety line behavior | Canvas & Output Dimensions |
---|---|---|---|---|
Undefined | Undefined | No | No dashed safety line is displayed on the canvas. | Matches configured the document width & height dimensions. |
Undefined | Undefined | Yes | Displays a dashed safety line on the canvas marking the original width & height boundaries, plus adds a 38px bleed area to the outside edges of the document. | Document width & height dimensions + default 38px bleed area on each side. |
Undefined | Integer > 0 | Hidden | Shows the safety line safetyMargin pixels INSIDE the original document boundaries indicating the safe zone. | Document width & height dimensions + default 38px bleed area on each side. |
Integer > 0 | Integer > 0 | Hidden | Shows a dashed safety line that is safetyMargin pixels INSIDE the original document boundaries, indicating the safe zone. The original document boundaries are not displayed in any way, and a second dashed line indicating the start of the bleed area is NOT displayed. | Document width & height dimensions + bleedMargin px on each side. |
Integer > 0 | Undefined - Default 0px | Hidden | Shows a dashed safety line over top of the original width & height of the document, then adds a bleed area that is bleedMargin pixels wide OUTSIDE of the original document dimensions. | Document width & height dimensions + bleedMargin px on each side. |
Crop Marks
Whenever a safety line is displayed on the canvas, the editor will automatically add crop marks to the finalized design when completed in the canvas. The crop marks, also known as cut marks, add an additional 37px of margin to allow for the crop marks on the document. The crop marks are placed outside of both the original dimensions of the document and any bleed margin that you configured. To prevent crop marks from being added, set the hideCropMarks LaunchOption when launching the editor.
SDK Renderer GraphQL Service
Overview of the Renderer
The Shutterstock Renderer Service provides a way for backend systems to render an exported design from the Create SDK when partners cannot load the Create SDK into the browser to do the rendering. This may be necessary when multiple systems provide an end-to-end flow with separate systems for each step. For example, a partner may load the Create SDK in the browser to empower a user to draft a design. Before navigating to a shopping cart system, the partner can export the design and safely unload the SDK. Once the user agrees to pay for stock content, the partner can license the content via the Shutterstock licensing API. Fulfillment systems can then provide the SDK-exported design and licensed content to the Renderer service to get a completed design without having to reload the full SDK into a browser.
Service Description
The Renderer API publishes GraphQL resolvers for interacting with the service. Partners can create a rendering task and subsequently query the status of their tasks for completion. The API takes a Shutterstock auth token, a Create SDK-enabled API key and the matching referer for the partner’s registered API application. The Renderer supports customizable properties including output format, image quality, and asset replacements. Each request is identified by a unique ID, and undergoes various statuses - from pending to completed or failed. When completed, the service returns a URL to the rendered image, valid for 24 hours.
Getting Started with the Renderer
If you are already familiar with GraphQL, you may skip over these sections introducing it and go right to the implementation section.
Introducing GraphQL
Background
GraphQL, developed by Facebook in 2012 and open-sourced in 2015, represents a query language and runtime that enables clients to request exactly the data they need, nothing more, nothing less. It emerged as a response to the limitations of REST APIs, offering a more efficient and powerful alternative, particularly for complex, interconnected data schemas. Contrary to REST, which uses different endpoints for different resources, GraphQL utilizes a single endpoint for all interactions. The type system and schema integral to GraphQL facilitate the precise querying of data, including nested resources, potentially reducing the amount of data transmitted over the network and improving application performance.
Understanding GraphQL
As you work to incorporate GraphQL into your project, it's essential to grasp the fundamental aspects that differentiate it from REST APIs and how these features can be leveraged to efficiently fetch data. In this section, we will introduce the basic concepts and the structure of a GraphQL query, providing a solid foundation to build upon as you move forward.
Basic Concepts
Query Language: GraphQL operates through a query language that allows clients to describe the exact structure of the data needed, facilitating precise and efficient data retrieval. Single Endpoint: Unlike REST APIs, GraphQL uses a single endpoint to handle all requests, simplifying the API structure and making it easier to manage and maintain. Schema & Types: Central to GraphQL is its schema, a blueprint that defines the types of data that can be queried and their relationships. The schema provides a contract between the client and server, ensuring data consistency and clarity on the available operations.
Structure of a GraphQL Query
Understanding the structure of a GraphQL query is vital in effectively leveraging its capabilities. Here's a breakdown of the essential components of a query: Field: At its simplest, a query is made up of fields. A field asks for a specific piece of information from the API. Arguments: Fields can take arguments, allowing you to pass variables and get varied responses based on the inputs. Aliases: When you need to query the same field with different arguments, aliases come in handy to avoid conflicts and streamline the query process. Operation Name: While not compulsory, specifying an operation name helps in organizing and identifying your queries, especially when you have multiple queries in your API. Variables: Variables allow you to reuse the same query but with different inputs, making your queries more dynamic and adaptable.
Setting up the GraphQL Environment
Integrating GraphQL into a browser and C# environments can be straightforward, especially with the aid of client libraries that encapsulate much of the complexity. Here, we'll guide you through setting up your environment to make GraphQL requests, either through specialized client libraries or by constructing raw web requests.
Tools and Libraries
While raw web requests can be used to interact with a GraphQL API, utilizing client libraries can significantly streamline the process and provide additional helpful features. Here are some suggestions for both C# environments and browser environments. C#-based libraries GraphQL.Client: A simple and modern library that makes it easier to integrate GraphQL APIs into your C# application. Hot Chocolate: A .NET platform for building GraphQL servers and clients, offering a wide array of features and extensive documentation. SAHB.GraphQLClient: A client library which allows you to easily execute queries against a GraphQL endpoint in a strongly typed manner.
Browser-based
The point of the Renderer service is to allow non-browser-based systems to render final designs without needing to load the SDK and design into the browser. If the use case you are solving requires a browser, you should consider using the SDK directly rather than using the Renderer service. Also, while browsers can make calls to the Renderer service, the service requires an authentication token. Storing auth tokens in the browser is not recommended best practice due to security concerns. Apollo Client: A popular choice that offers a comprehensive suite of features, including caching and state management. Its extensive documentation and community support can be a boon for developers. Relay: Developed by Facebook, it provides an efficient, predictable, and powerful way to build data-driven applications with React. GraphQL.js: A straightforward JavaScript GraphQL client, enabling you to make requests and handle responses with ease.
Calling the Renderer
To call the Renderer service, you must create a GraphQL client and authenticate that your requests are on behalf of a properly configured Shutterstock API account and application.
Authorization
In order to authenticate requests, you first need a Shutterstock API application that is enabled for the Create SDK. From this application, you will need:
- API key, also called the consumer key
- A valid bearer token
- One of the fully qualified domain names configured in the referrer field of the application
Configuring Create SDK on Your Application
Your partner manager should be working with you to configure an application for the Create SDK. If you want to use that same application and API key for core Create SDK and the Renderer Service, you can proceed to obtaining the bearer token. If, however, you want to use a separate API application and consumer key for working with the Renderer, contact your partner manager who can set up a new or add the Create SDK product to an existing application. To manually confirm if the application & consumer key you want to use with the Renderer has Create SDK enabled, log into your Shutterstock account and view the details tab of the app you want to use.
Getting the API Key
The API key you send to the Renderer service is called consumer key on the API application’s authentication tab.
Getting Bearer Tokens
To create a token, you can either follow the oAuth guide here or log into your Shutterstock account, find the application you want to use and click on “generate token” on the Authentication tab.
Matching Referer
Calls to the Renderer service must also pass a fully qualified domain name that matches one of the configured referrer values on your API application. If you don’t remember what values were configured, contact your partner manager. You can also inspect this yourself by editing the application you are trying to use. The referrer field contains a comma-separated list of fully qualified domain names (FQDN) including the host, domain and, if applicable, the port number. If you do not find the FQDN you want to use in the referrer field, you can add it manually or follow up with your partner manager for help.
GraphQL Endpoint
The production endpoint for the service is: shutterstock.com/graphql
Schema
After deciding what library you want to use to work with the Renderer GraphQL endpoint, you will need to provide the Renderer’s schema file to configure the tool for making requests. GraphQL schemas provide an exact description of the data that can be asked for - what fields can we select? What kinds of objects might they return? What fields are available on those sub-objects? Schemas are written in GraphQL schema language, a simple language for defining the schema that is independent of the service implementation, allowing the backing service to be written in one of many different languages. Below is a copy of the graphql schema for the Create SDK to create and read render requests.
"""
An sdk render request can be in one of the following states
"""
enum CreateSdkRenderStatus {
"""
The render request has been received and is waiting to be processed
"""
PENDING
"""
The render request is being processed
"""
RENDERING
"""
The render request has been completed successfully
"""
COMPLETED
"""
The render request has failed
"""
FAILED
}
"""
A problem with the request
"""
type CreateSdkRenderProblem {
"""
The problem description
"""
message: String!
"""
The problem code from apollo-server-errors package
"""
code: String
}
"""
Encapsulates the response and any problems with the request
"""
type CreateSdkRenderResponse {
"""
The response data (will be null if there are problems)
"""
data: CreateSdkRender
"""
Any problems with the request
"""
problems: [CreateSdkRenderProblem!]
}
"""
A render request that returns the current status of the render and the url to the rendered image
"""
type CreateSdkRender {
"""
The ID of the render request to be used in subsequent requests
"""
id: ID!
"""
The url to the rendered image
"""
urls: [String!]
"""
The content type of the rendered image
"""
contentType: String
"""
The current status of the render request
"""
status: CreateSdkRenderStatus!
}
"""
The available render types
"""
enum CreateSdkRenderType {
PNG
JPEG
PDF
}
"""
A replacement for an asset in the design
"""
input CreateSdkRenderReplacement {
"""
The asset_token of the asset to be replaced such as a watermarked image or user upload
"""
key: String!
"""
The replacement value, which should be a publicly accessible url
"""
value: String!
}
"""
The input data for a render request
"""
input CreateSdkRenderInput {
"""
The format of the rendered image
"""
type: CreateSdkRenderType
"""
If type is JPEG, the quality of the rendered image
Between 0 and 100
Default is 80
"""
jpegQuality: Int
"""
Assets to be replaced in the design with pubicly accessible urls
"""
assetReplacements: [CreateSdkRenderReplacement!]
"""
The design blob
"""
createDesignBlob: String
"""
Used to request a specific version of the create application to be used for the render
If not specified, the latest version will be used
"""
sha: String
}
type Mutation {
"""
Create a render request
The response will contain the id of the render request to be used in subsequent requests
The render request will be processed asynchronously and the response will contain the current status of the render and the url to the rendered image
The url to the rendered image will be valid for 24 hours
The apiKey must be a valid api key for the Create SDK
The referer must be a referer set on the application associated with the api key
"""
createSdkRender(
data: CreateSdkRenderInput!
apiKey: String!
referer: String!
): CreateSdkRenderResponse!
}
type Query {
"""
Get a render request by id
The response will contain the current status of the render and the url to the rendered image
The apiKey must be a valid api key for the Create SDK and must match the api key used to create the render request
The referer must be a referer set on the application associated with the api key
"""
createSdkRender(
renderId: ID!
apiKey: String!
referer: String!
): CreateSdkRenderResponse!
}
Making Raw Web Requests
Despite the conveniences offered by client libraries, sometimes you might prefer or need to make raw web requests. GraphQL is not bound to any specific library or tool. You can make HTTP POST requests with a JSON payload to interact with a GraphQL API. Here’s a generalized structure of how such a request would look:
C# (HttpClient)
using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "your-api-token-here"); var queryPayload = new { query = @" mutation CreateRenderRequest($input: CreateSdkRenderInput!, $apiKey: String!, $referer: String!) { createSdkRender(data: $input, apiKey: $apiKey, referer: $referer) { data { id } problems { message } } } ", variables = new { input = new { // ... your serialized CreateSdkRenderInput fields go here } } }; var response = await client.PostAsJsonAsync("https://shutterstock.com/graphql", queryPayload); var result = await response.Content.ReadAsStringAsync(); Console.WriteLine(result); }
Known Issues
Custom Uploads
Custom user uploads in the SDK DesignExport cannot be accessed by the Renderer without additional steps. By default, user-uploaded images in the Create SDK will be immediately inserted into the document and subsequently serialized into an exported design so that it can be reloaded later. Those uploads are only available in that unique DesignExport, and must be uploaded to the SDK again for each new design where they want to use the same uploaded image. If the DesignExport is given to the Renderer Service, these uploads will be ignored. However, some partners provide their own Asset Management API so their users have a persistent store of uploads each time they use the Create SDK editor. Partners set the customUploadUrlAdjustment LaunchOption to manipulate the host, path and the querystring of any uploaded asset URL to, for example, provide a short-lived token to authorize the requests from Create SDK to get the assets. This data may be expired by the time the Renderer obtains the DesignExport, so the partner must provide updated URLs when calling the renderer. Uploads can then be swapped out of a DesignExport when calling the Renderer:
- Before unloading the core SDK, the partner must call getUploadsInDocument to get the list of user uploads.
- When calling the renderer, provide assetToken as the key and replacement URL as the value for each uploaded asset in the assetReplacements array.
- Ensure CORS settings are properly configured on the image host to allow requests for the assets. The requests will be coming from the www.shutterstock.com origin.
Release notes
Release: v1.0.59 - May 2024
- features.templates.hidden - New launchEditor option that exposes the ability to hide the templates panel from users so they do not see the Shutterstock templates.
- New launchOptions hideAddStockContentMessage and addStockContentMessage. These allow the partner to either customize the message shown in the notification toast when stock content is added to a document in the editor or to suppress the notification completely.
- onAddPageToDocument/onDeletePageFromDocument - New methods that allow client to monitor events for adding and removing pages from the document.
- Bug fixes related to broken panels with missing images in the editor, and event cleanup process not correctly clearing out and re-enabling events.