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:

  1. identify the partner making the request
  2. validate that the integration has the necessary permissions to use the CreateSDK
  3. 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.

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.

NameTypeRequiredDescription
apiKeyStringYesPartner consumer key to authenticate use of editor
containerHTML ElementNoElement in which to embed the Editor. If not provided, this will default to showing the editor in a modal over top of your webpage.
debugBooleanNoTruthy value will turn on console logging for enhanced info in the browser’s developer tools.

Returns

CreateSDK object. The SDK has the following structure:

NameTypeDescription
closeEditorFunctionAllows the consuming application to remove the SSTK editor iFrame from the DOM, freeing up resources consumed.
FileTypeObjectCollection of file types that can be used to designate the type of file to export.
exportDesignFunctionExports a JSON file representing the design with unlicensed images.
getDesignFunctionFunction to get a preview of the document currently in the canvas.
getUnlicensedAssetsFunctionReturns an array of unlicensed assets used in the current design document, including the image IDs for use with the SSTK public API for licensing.
hideFunctionUses CSS to hide the SDK container and editor, although the SDK remains loaded in memory. Show method will re-display the editor.
JpegQualityObjectString bag with values guaranteed to work with the launchEditor option jpegQuality.
launchEditorFunctionFunction that opens a new or existing Create editor document
nameString“create-integration-library/integration”
offFunctionDetaches event handlers that were previously listening.
onFunctionAttaches handlers that listen for any supported event each time they are executed.
onceFunctionAttaches handlers that are executed once for any supported event, and then is automatically detached.
replaceUnlicensedAsSetsFunctionSwaps images in the design for images URLs passed in, based on JavaScript matching asset IDs. Intended for replacing watermarked images with licensed images.
setCustomUploadUrlAdjustmentFunctionConfigures 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.
setUploadsFunctionInitializes the uploads controls’ asset manager section with images passed from the integrator.
showFunctionIf 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.

CodeLanguage
csCzech
daDanish
deGerman
elGreek
enEnglish
esSpanish
fiFinnish
frFrench
hiHindi
huHungarian
idIndonesian
itItalian
jaJapanese
koKorean
mrMarathi
nbNorwegian
nlDutch
plPolish
ptPortuguese
roRomanian
ruRussian
svSwedish
taTamil
teTelugu
thThai
trTurkish
viVietnamese
zhSimplified 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

NameTypeRequiredDescription
assetUrlStringNoURL to an image that can be used as a starting point for a new document
bleedMarginNumberNoValue 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.
bypassUploadProcessingStringNoWhen 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.
ctaLabelStringNoDefines 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.
customUploadUrlAdjustmentObjectNoWhen 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.widthStringNoWidth of the document canvas when not opening a template or existing doc
document.heightStringNoHeight of the document canvas when not opening a template or existing doc
document.lockCanvasDimensionsBooleanNoInstructs 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.maxPagesNumberNoMaximum number of pages allowed in the editor for a single document. If undefined, defaults to 30 pages.
enableUserAnalyticsBooleanNoBy 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.hiddenBooleanNoHides the templates button and panel from users so they cannot use Shutterstock templates. Because templates are hidden, the color selection tool will be opened by default when the editor is first launched.
fileTypeStringNoDefine the file type for the exported design. Default is PNG. Can be a string or use a value from the exported FileType enum.
fullStoryKeyStringNoPass 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.
hideCropMarksBooleanNoInstructs 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.
hideStockContentBooleanNoHides the side panel that allows users to search and browse through Shutterstock’s image library (and other stock content in the future).
hideTopBarBooleanNoHides 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.
imageIdNumberNoOptional image used as starting point for the document. Ignored if templateId is supplied.
importAssetDesignExportNoAn DesignExport object from a previously provided by the exportDesign method.
jpegQualityStringNoString value for low, medium, high or use the JpegQuality enum exported from the SDK.
languageStringNoPass a 2 character language code to control the language of the editor UI. Default is english. See supported languages here.
onEditorOpenFunctionNoCallback/handler for EditorOpened event triggered when the editor app begins loading into the iFrame
onEditorInteractiveFunctionNoFunction that runs when the EditorInteractive event is fired and the editor is fully loaded
onDesignCompleteFunctionNoFunction that listens for the DesignComplete event when a user completes their design by clicking on the primary CTA
onEditorCloseFunctionNoFunction that runs when the EditorClose event is fired
onErrorFunctionNoFunction that runs when the Error event is fired and receives an ErrorDetails object.
overrideUploadHandlerBooleanNoInstructs 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.
preventEditorCloseBooleanNoPrevent Editor from closing when the user clicks the close button
safetyMarginNumberNoValue 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.
templateIdStringNoOptional 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

NameTypeRequiredDescription
forceEditorCloseBooleanNoCloses 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.

NameTypeRequiredDescription
versionStringYesProvides forward compatibility if Shutterstock ever needs to update the schema of the export.
document.heightNumberYesHeight in pixels
document.patchesStringYesBase64 representation of patches describing the separate elements of the design.
document.titleStringYesGenerated name of the design document.
document.uploadedAssetsB64BlobsObjectNoKey/value pairs of serialized images included in the design. Default value is undefined.
document.widthNumberYesWidth 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.

NameTypeRequiredDescription
fileTypeStringNoDefine the file type for the exported design.Default is PNG. Can be a string or use a value from the exported FileType enum.
jpegQualityStringNoString value for low, medium, high or use the JpegQuality enum exported from the SDK.
documentWatermarkOptionsDocumentWatermarkOptionsNoDefines 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.

NameTypeRequiredDescription
urlStringYesPublic 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.
isTiledBooleanNoIndicates if the image should be tiled. Default is false.
positionStringNoPredefined 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.
relativeSizePercentageNumberNoThe 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
NameTypeRequiredDescription
image_idStringYesSent as a string property, this is typically a numerical ID value.
asset_tokenStringYesToken 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
NameTypeRequiredDescription
imageIdStringYesThe id of the uploaded image
assetTokenStringYesThe 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

NameTypeRequiredDescription
EventNameStringYesThe name of the event you are attempting to set or remove a handler for. This is NOT case sensitive.
pointerToHandlerFunctionYesEvent 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
NameTypeRequiredDescription
allotment_chargeNumberNoThis data is part of the response from the licensing API and is not strictly required.
download.urlStringYesThe download Object has a single property, the url, which is a string.
image_idStringYesID of the licensed image, necessary for the swap action to work. Should match the unlicensed image you are replacing
license_idStringNoThis 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.

NameTypeRequiredDescription
prefixStringYesPortion of the URL that should occur before the main ID/name of the original image.
suffixStringYesPortion 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.

NameTypeRequiredDescription
heightNumberYesHeight in pixels
idStringYesUnique ID for the asset
labelStringYesA friendly label for the asset
providerStringNoThis will always be ‘sdkUpload’.
thumbnailHeightNumberNoThumbnail height in pixels
thumbnailUrlStringYesURL to a thumbnail shown in the Uploads section of the editor.
thumbnailWidthNumberNoThumbnail width in pixels.
urlStringYesURL to the full sized image for use when inserting an image to the canvas.
widthNumberYesWidth 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.

NameTypeRequiredDescription
currentPageIndexNumberYesIndex of the current page of the (multi-page) document
heightNumberYesHeight of the output
idStringNoThe ID of the users design. If the user is anonymous we won’t have a design ID
pagesDesignPageYesFor 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.
titleStringYesThe title of the user’s design
widthNumberYesWidth 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.

NameTypeRequiredDescription
imageBlobBlobYesThe blob for downloading the user design
encodedArrayUint8ArrayYesDesign encoded into an array
fileSizeNumberYesSize of the output
heightNumberYesHeight of the output
urlStringYesThe url for displaying the user design
widthNumberYesWidth 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 NameOption nameFired when…
DesignCompleteonDesignCompleteUser clicks the call-to-action button to export the design
EditorCloseonEditorCloseImmediately after the call-to-action OR if the user clicks the close button.
EditorOpenedonEditorOpenedEditor react app begins bootstrapping itself in the container provided.
EditorInteractiveonEditorInteractiveEditor is fully loaded and ready to use
UserInteractiononUserInteractionEchoes telemetry events collected by Shutterstock back out through the SDK to the partner.
RequestAddAssetToCanvasonRequestAddAssetToCanvasUser attempts to add an asset from the asset manager into the canvas.
RequestDeleteAssetonRequestDeleteAssetUser attempts to delete an asset that is shown in the asset manager section of the Uploads control.
RequestUploadAssetonRequestUploadAssetUser clicks the Uploads button in the Uploads sidebar control.
StockContentUpdatedonStockContentUpdatedUser adds or removes stock content such as images to the canvas.

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
NameTypeRequiredDescription
idStringYesA string identifying the error type, if known.
messageStringYesA human readable description of the error that occurred.
dataObjectNoWhen 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.

IDMessageData
webgl-errorWebGL 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.

NameTypeRequiredDescription
userStatusStringYesFor 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

  1. You must set the overrideUploadHandler launch option to true in order for this event to be fired.
  2. 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
NameTypeRequiredDescription
itemAssetYesThe asset being added.
positionFloat32ArrayNoOptionally 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.
typeStringYesValue 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
NameTypeRequiredDescription
itemAssetYesThe 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
NameTypeRequiredDescription
fileFileYesStandard File object describing the file the user wants to upload.
heightNumberYesHeight of the upload in pixels.
thumbUrlStringYesURL for a thumbnail of the uploaded image.
widthNumberYesWidth 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.

NameTypeRequiredDescription
addedstringYesArray of image IDs that were added to the canvas.
inUsestringYesArray of all image IDs that remain in the canvas after the operation
removedstringYesArray of image IDs that were removed from the canvas.
updateTypestringYes‘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.

NameTypeRequiredDescription
eventNameStringYesFriendly name of the event. See EventTypes table.
resultsMatchedBooleanNoIndicates whether search results were returned on a search event.
searchTermStringNoThe 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.
eventNameDescription
Design Complete CTA2Primary call to action button clicked
Images TabImages tab clicked
Upload clickedUpload Click
Upload ErrorUpload Error
Upload successUpload success when images load into the tray
Images TabStock Photos tab clicked
Catalog Tab2Catalog tab clicked
Uploads TabUploads tab clicked
Images Search2Images search conducted
Text TabText tab clicked
Shapes TabShapes tab clicked
Shapes Search1Shapes search conducted
Graphics TabGraphics tab clicked
Graphics Search1Graphics search conducted
Drawing TabDrawing tab clicked
Predict Tab2Predict tab clicked
Smart Resize TabSmart resize tab clicked
Canvas TabCanvas tab clicked
Templates TabTemplates tab clicked
Templates Search1Templates search conducted
Collage TabCollage tab clicked
Change Canvas ColorChange canvas color clicked
Add BG image - ComputerAdd background image from computer clicked
Add BG image - Stock PhotosAdd background image from stock photos clicked
BG Image Search2Background image search conducted
TexturesTextures control clicked
EffectsEffects control clicked
Rotate LayerRotate layer control clicked
Flip Layer VerticallyFlip vertically clicked
Flip Layer HorizontallyFlip horizontally clicked
Delete LayerDelete layer clicked
SettingsSettings gear clicked
Align & Snap - EnabledAlign & Snap feature enabled
Align & Snap - DisabledAlign & Snap feature disabled
Show bleed marks - EnabledShow bleed marks enabled
Show bleed marks - DisabledShow bleed marks disabled
Show grid on canvas - EnabledShow grid on canvas enabled
Show grid on canvas - DisabledShow grid on canvas disabled
UndoUndo clicked
RedoRedo clicked
LayersLayers 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.

  1. 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.
  2. Once the user selects an image they want, use your application’s purchase flow to trigger the API client to license the image.
  3. Configure the clean URL provided by the API as the assetUrl property of launchEditor’s opts object.
  4. To prevent the user from adding unlicensed content after the editor opens, you must also set hideStockContent property to true.
  5. When the Create Editor is launched, the user will see the unwatermarked image in the editor as they build their design.
  6. All entry points to search for unlicensed stock content will be hidden from your application’s users.
  7. 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.

  1. Partner provides StockContentUpdated event handler (launch option or on method)
  2. 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.
  3. 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
  4. After licensing, you can repeat the steps if additional licensed images are added
  5. 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.

  1. Set launchEditor option preventEditorClose to true and launch the create editor using a blank canvas as the starting point.
  2. The user searches for images in the images panel on the left side, and can insert watermarked images into their design.
  3. Partner configures a handler for the DesignComplete event
  4. 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().
  5. Call the Shutterstock licensing API with the details of each unlicensed asset and collect the response data, including urls to the clean, unwatermarked images.
  6. Call replaceUnlicensedAssets, passing the assets (gatekeeperUrls in the old SDK), including IDs and URLs, back into the CreateSDK method.
  7. Call getDesign to get a new download of the design without watermarks.
  8. Call closeEditor to dispose of the SDK from the browser, passing the forceEditorClose option as true.
  9. 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:

  1. First, export the design as a JSON file & the list of images that need to be licensed
  2. Unload SDK to completely
  3. Proceed through partner workflow
  4. Directly use Shutterstock’s licensing API to license the images and save the information
  5. Reload the Create SDK into a hidden container element, designating the JSON file as a the launchEditor option importAsset
  6. Provide an array of imageIds and asset URLs for the licensed images to the SDK method replaceUnlicensedAssets
  7. Call getDesign to get the finalized design
  8. 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.

  1. User goes through design experience with the Create SDK.
  2. 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.
  3. Proceed through partner workflow, shopping cart, payment experience, etc.
  4. Partner backend system calls Shutterstock’s licensing API to license the necessary images.
  5. 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.
  6. Renderer Service will return a renderId in response.
  7. Partner backend system will poll the renderer finishing service, passing the renderId and other necessary information to identify the partner.
  8. 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.

TypeUpload File ExtensionsMimeRequestUploadAsset Converted To
JPEG Images.jpeg, .jpgimage/jpegNot applicable
Portable Network Graphics.pngimage/pngNot applicable
Scalable Vector Graphics.svgimage/svg+xmlNot applicable
Adobe Portable Document Format.pdfapplication/pdfUp 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:

  1. Retrieve a list of available assets for the current user
  2. Upload a new asset to the user’s collection
  3. 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: 1) 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.

bleedMarginsafetyMarginBleed line in canvas optionsDashed safety line behaviorCanvas & Output Dimensions
UndefinedUndefinedNoNo dashed safety line is displayed on the canvas.Matches configured the document width & height dimensions.
UndefinedUndefinedYesDisplays 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.
UndefinedInteger > 0HiddenShows 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 > 0Integer > 0HiddenShows 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 > 0Undefined - Default 0pxHiddenShows 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:

  1. API key, also called the consumer key
  2. A valid bearer token
  3. 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:

  1. Before unloading the core SDK, the partner must call getUploadsInDocument to get the list of user uploads.
  2. When calling the renderer, provide assetToken as the key and replacement URL as the value for each uploaded asset in the assetReplacements array.
  3. 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

Date: 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.
  • Bug fixes related to broken panels with missing images in the editor, and event cleanup process not correctly clearing out and re-enabling events.

Documentation Published: Feb 2024