NAV Navbar
php shell
  • Custom Gateway Personalisation Platform APIs
  • For Retailers
  • For Suppliers
  • GraphQL
  • Custom Gateway Personalisation Platform APIs


    This document describes some of the APIs that the personalisation platform exposes.

    Which APIs you might be interested in depend on whether you are a retailer or a supplier.

    Please select from the left hand side.

    Our IP Range

    Our IP range is listed below if you would like to or need to white list us.

    For Retailers


    This guide covers how to integrate your website into the Custom Gateway personalisation platform.

    The guide is split into two key sections.


    The Smartlink guide covers how to integrate personalisation apps into your website.

    You might not need to follow this part depending on your requirements.


    The Order-iT guide covers how you can send orders to Custom Gateway's Order Manager System, from where the orders will be routed to the correct supplier.

    You will definitely need to follow this part.

    Virtual Product Creator

    The Virtual Product Creator (VPC) guide shows how you can embed Custom Gateway's VPC tool into your website. The VPC allows your end users to create their own sellable product designs within the platform without ever leaving your site.

    You might not need to follow this part depending on your requirements.

    Before Starting

    Before starting either section, you will need the following information:

    Both will be provided to you by Custom Gateway. Please contact us for more details.

    The VPC tool has additional requirements that are documented in the VPC section.

    Note on Code Samples

    The shell code samples to the right assume that you have access to the curl command line tool.

    Custom Gateway's personalisation apps are provided in the form of a URL which can be embedded via an iframe into your website.

    Different apps exist and you may require different URL parameters depending on your use case.

    The below URL is a bare bones example of our standard offering:

    The URL can be broken down into a few key parts:

    Part Description
    acp3_2 The app that you would like to use
    en_GB The app locale
    default The app configuration
    p=1007644 The ID of the product to load
    r=2d-canvas The renderer to use. Typically this would either be 2d-canvas for 2D products or webgl for 3D products. multi can be used to load both renderers however doing so may slightly reduce loading speeds
    guid Your unique company reference ID

    Adding to Cart

    When a user clicks on an "Add to Cart" button within a personalisation app, the app will create a "print job" that represents the user's design.

    Subsequently, the app will then pass control back to the eCommerce website via the use of a callback URL.

    The callback URL serves two key purposes:

    The callback URL is specified via the ep3dUrl personalisation app URL parameter.

    For example:

    Handling The Callback

        "printJobRef": "CF921CF10F59B3A258",
        "thumburl": ""

    The app sends data to the callback URL via the use of a traditional HTML form data POST (with the application/x-www-form-urlencoded content type).

    The only field included in the request is data, the value of which is a JSON encoded object.

    The object will contain many fields, the most important of which are:

    Name Type Description
    printJobRef string A unique alphanumeric identifier for the user's print job.
    thumburl string A URL to a thumbnail of the user's design.

    Breaking Out Of The Iframe = "";

    The callback URL also needs to be able to break out of the personalisation app's iframe.

    This can usually be achieved via a simple JavaScript one liner (see right).

    Restoring a User's Design

    If you wish to allow the user to edit their design later on, it can be loaded again by passing the value of printJobRef back to the personalisation app via the pj (print job) URL parameter.

    For example:

    When the user clicks "Add to Cart" again in the app, the callback URL will be called as normal but with a new value for printJobRef.


    A user can save their design, allowing them to reload it at a later date. You need to provide a callback URL which the user is redirected to after saving. The callback URL allows you to record the information needed to reload the user's design.

    The following URL enables the save button:

    Handling The Callback

    Once the user has clicked save, the app will make a application/x-www-form-urlencoded form POST to your callback URL.

    The POST will include a data field, the value of which is a JSON encoded object.

      "created_from": "",
      "ref": "44EEEA752F59C3A3B1",
      "thumbnails": [
          "name": "thumbnail",
          "url": "",
          "description": ""
          "name": "thumbnail_60",
          "url": "",
          "description": ""

    The object will contain many fields, the most important of which are:

    Name Type Description
    created_from string The original app URL
    ref string A unique reference code needed to reload the user's design
    thumbnails array A collection of thumbnails for the user's design which you might like to display somewhere

    Reloading The Saved Design

    The user's design can subsequently be loaded by taking the value of the created_from field and appending &ps= and the value of the ref field. For example:


    postMessage API

    Personalisation apps are capable of sending messages that represent certain events. It's possible for the app's parent frame to receive and act upon these messages.

    The HTML5 postMessage API is used by the app to send messages.

    Currently the available events are undocumented and we do not provide any guarantees to their future behaviour with the exception of the events listed below.

    The meo and mei URL parameters are used to ensure that the parent window only acts on messages from the correct iframe instance. The app will not send messages if either meo and mei are not specified. For security reasons it is also important to ensure that the message is from the origin.

    <!DOCTYPE html>
            <iframe style="border: 1px solid red; width: 100%; height: 50px;" src="" id="app"></iframe>
            let iframeOrigin = "";
            let iframeUrl = iframeOrigin + "/s/app/acp3_2/en_GB/default.html#p=1007644";
            let meo = location.origin;
            let mei = Math.random().toString(16).substr(2);
            window.addEventListener("message", e => {
                if(e.origin == iframeOrigin && == mei) {
                    switch( {
                        case 'IFRAME_RESIZE':
                   = + "px";
                        case 'ADD_TO_CART_CALLBACK':
                            var printJobRef =[0].ref;
            let iframe = document.getElementById('app');
            iframe.src = iframeUrl + "&meo=" + meo + "&mei=" + mei;


    The IFRAME_RESIZE event can be used to automatically resize the iframe on your site so that all content fits within it without the need for scroll bars within the iframe.


    The ADD_TO_CART_CALLBACK event is triggered after a print job has been created, signalling that the user's design should be added to the site's shopping cart and the user redirected away from the personalisation app.

    The event's data contains a separate item for each print job that was created. Usually, each item would be added to the cart separately however you may wish to consolidate them depending on your exact use case.

    Each item element of the data property contains the below fields.

    Name Type Description
    ref string The unique reference code for the created print job.
    sku string The product's SKU
    thumbnails array A collection of thumbnails for different views of the product (including the user's design)


    The Order-iT API has a single endpoint:

    Rate Limiting

    Requests to the API are rate limited to 1000 requests per hour and 10000 requests per day.

    A 400 HTTP status code will be returned in the event of the limit being exceeded, please check the error code table for more details.

    If you need this limit to be increased on a per account basis, please contact us.

    IP Restrictions

    For additional security, an IP white list can be enabled for your company reference ID.

    Any requests made using your company reference ID and API key from an IP address that isn't white listed will be rejected.

    Please contact us to set up IP restrictions, quoting the IP addresses that you will be sending requests from.

    Migrating from 2.1

    This document refers to the 2.2 version of the API.

    The endpoints for version 2.1 are or

    Version 2.2 is mostly backwards compatible with 2.1 however there are some key differences:


    curl ""
      -H "Authorization: Basic COMPANYREFID:APIKEY"

    Make sure to replace COMPANYREFID and APIKEY with your company reference ID and API key respectively.

    If authentication fails, the following JSON response body will be returned, along with a 400 HTTP status code:

        "error": {
            "message": "Authentication failed",
            "code": 50000

    To authenticate against the API, include the Authorization HTTP header with every request.

    For example,

    Authorization: Basic COMPANYREFID:APIKEY

    Submitting an Order

        -H "Content-Type: application/json"\
        -H "Authorization: Basic COMPANYREFID:APIKEY"\
        "external_ref": "TEST001",
        "sale_datetime": "2017-01-01 12:00:43",
        "shipping_company": "Custom Gateway Ltd."
        "shipping_address_1": "Pinewood Court",
        "shipping_postcode": "SK10 2XR",
        "shipping_country_code": "GB",
        "items": [
                "external_ref": "ITEM001",
                "quantity": 1,
                "type": 2,
                "print_job_ref": "CF921CF10F59B3A258"

    A successful request will result in an array of orders being returned:

            "id": "...",
            "ref": "...",
            "external_ref": "TEST001",
            "has_error": false,
            "error_message": "",
            "items": [
                    "id": "...",
                    "ref": "...",
                    "external_ref": "ITEM001"

    Please see the errors section for more information on the has_error and error_message fields.

    An order can be created by sending a JSON encoded order object via a POST request to the API.

    HTTP Request


    Order Object

    Name Type Description
    external_ref string A unique reference code that can be used to identify the order in your system.
    company_ref_id integer A unique company ID (supplied by Custom Gateway). Must correspond to the API key provided via the URL.
    sale_datetime string The date and time (must be UTC) of when the order was placed in the format YYYY-MM-DD HH:MM:SS (i.e. 2013-08-07 10:54:23)
    required_dispatch_date string The required dispatch date for the order in the format YYYY-MM-DD (i.e. 2013-08-07)
    customer_name string Customer's full name.
    customer_email string Customer's contact email.
    customer_telephone string Customer's contact telephone number.
    shipping_company string Shipping address company.
    shipping_address_1 string Shipping address 1.
    shipping_address_2 string Shipping address 2.
    shipping_address_3 string Shipping address 3.
    shipping_address_4 string Shipping town or city.
    shipping_address_5 string Shipping region.
    shipping_postcode string Shipping post or ZIP code.
    shipping_country_code string ISO 3166-1 alpha-2 country code.
    shipping_method string Shipping method.
    shipping_carrier string Shipping carrier.
    shipping_note_url string An optional URL for a specifying an external shipping note. If no value is provided then a basic shipping note will be generated. The URL must begin with either http:// or https://.
    billing_customer_name string Billing name.
    billing_company string Billing address company.
    billing_address_1 string Billing address 1.
    billing_address_2 string Billing address 2.
    billing_address_3 string Billing address 3.
    billing_address_4 string Billing town or city.
    billing_address_5 string Billing region.
    billing_postcode string Billing post or ZIP code.
    billing_country_code string Billing country code.
    coupon_code string Coupon code.
    payment_trans_id string Payment transaction identifier.
    status_callback_url string Order status callback URL.
    items array An array of order items

    Item Object

    Name Type Description
    external_ref string A reference code that can be used to identify the order item in your system.
    sku string Product SKU.
    description string Product description.
    quantity integer Item quantity.
    colour string Item colour.
    size string Item size.
    textual_product_id integer Determines which CPP product the order item is for (can be used for either textual items or external URL items).
    type integer Item type code
    external_url string Required for external items (type 1). URL to an image which will be used for this item. This URL must be available until the order has been processed.
    external_thumbnail_url string Required for external items (type 1). URL to a thumbnail of an image which will be used for this item. This URL must be available until the order has been processed.
    print_job_ref string Required for print job items (type 2). A unique identifier linking this item to an already existing print job (created by the Custom Gateway product designer). The specified print job must have been created using a company ID which the current API key (URL parameter k) is permitted access to.
    print_on_demand_ref string Required for print on demand items (type 3). A unique identifier linking this item to a pre-created Print-on-Demand sample (created from within the Custom Gateway ACP).

    Item Type Codes

    Type Code Name Description
    1 External URL an item for which the artwork is specified as an external URL (not hosted by Custom Gateway).
    2 Print Job An item that can be associated with a print job created by the Custom Gateway product designer. Once an order has been created, any referenced print jobs will be set to "Paid" in the "Print Manager".
    3 Print-on-Demand An item that can be associated to a precreated Print-on-Demand sample.
    5 Textual Item An item that does not have artwork generated by Custom Gateway but does have textual personalisation.

    Status Callbacks

    Example callback URL request body

        "id": "...",
        "external_ref": "...",
        "ref": "...",
        "status": "...",
        "status_name": "...",
        "shipping_tracking": "...",
        "shipping_method": "...",
        "shipping_carrier": "..."

    Every time an order's status changes in OMS, the URL that you specified in the status_callback_url field will be sent a PUT request with a JSON encoded request body.

    Your status callback should return a 200 HTTP response code if successful. In the event of a 4XX or 5XX error response code, OMS will keep retrying the request up to a maximum of 5 times at 2 hour intervals. For statuses that you are not interested in, please be sure to still reply with a 200 response code to prevent unnecessary retries.

    Request Fields

    Name Type Description
    id integer The unique sequential ID for the order
    external_ref string The order reference number provided when creating the order
    ref string The unique alphanumeric reference code for the order
    status int The order's current status
    status_name string The name of the order's current status
    shipping_tracking string A shipment tracking ID. This field may be blank and its population is dependent on the product supplier.
    shipping_method string The shipping method that will be used for shipping the order
    shipping_carrier string The shipping carrier that will be used for shipping the order

    The shipping_carrier and shipping_method values may be different to what was specified during order creation. It is common for the values of both fields to mapped to different but equivalent values that the supplier is able to accept.

    For example, if Standard Delivery Next Day is specified as the shipping_method during order creation, it may be mapped to Royal Mail and RM1 for shipping_carrier and shipping_method by the supplier.


    Generally your status callback would not need to handle all possible order statuses.

    Status Code Status Name Description
    0 Unknown The status of the order is unknown.
    1† Received The order has been received by Custom Gateway.
    2 Unused Unused
    4 In Production (Only when batching is enabled) At least a part of the order has been downloaded by a fulfilment operative.
    8† Dispatched The order has been fully dispatched.
    32 QC Query The order has a QC query against it.
    64 Dispatched (Retailer Notified) Dispatch confirmation has been explicitly pushed to the retailer by Custom Gateway.
    128 Cancelled The order has been cancelled.
    256 On Hold The order is on hold.
    512 Sent to Supplier The order has been routed to an appropriate supplier.
    513 Received by Supplier The order has been confirmed as received by an appropriate supplier.
    515 Sent to Shipper The order details have been sent to a shipment courier (such as UPS or DPD).
    516† Received by Shipper The order details have been confirmed by a shipment courier.


    API Errors

    An example HTTP 400 error response body

        "error": {
            "code": 12345,
            "message": "A brief description of the problem"

    An example HTTP 500 error response body

        "error": {
            "code": null,
            "message": "An unexpected error occured 84C1E94BAF83D9C2A43CFCCBC04FE937"

    When the API encounters an error, it will return either a 400 or 500 HTTP status code along with a JSON encoded error response.

    The 500 HTTP status code indicates an internal error that should be reported to us. Please quote the full error message, including the long alphanumeric identifier.

    The JSON encoded error response will contain both an error code and a message.

    Error Code Meaning Solution
    0 Please check the error message for more details
    100 Invalid JSON Please check the error message for details on syntax errors etc.
    8000 No items At least one order item must be specified
    8001 An order already exists with the specified value for external_ref Ensure that external_ref is unique for each order
    8011 Invalid sale_datetime sale_datetime must be in the correct format and not in the future.
    8012 Invalid status_callback_url status_callback_url must be a valid URL and have a public DNS entry
    8013 Invalid country code shipping_country_code and billing_country_code must be valid 2 character ISO 3166-1 alpha-2 country codes
    8014 Invalid required_dispatch_date required_dispatch_date must be in the correct format and not in the past.
    8030 Order company_ref_id not specified
    8031 Order external_ref not specified
    8032 Order shipping_address_1 not specified
    8033 Order shipping_postcode not specified
    8034 Order shipping_country_code not specified
    8040 Invalid item type
    8041 Invalid item quantity quantity must be greater than 0
    8042 Invalid item attribute for a textual item The attribute must exist as an image or text area on the specified product
    8043 Invalid item print job The print_job_ref field must be valid and be a print job that is owned by your account
    8050 Item external_ref not specified
    8051 Item print_job_ref not specified
    8052 Item textual_product_id not specified
    8053 Item external_url not specified
    8054 Item external_thumbnail_url not specified
    50000 Either your company reference ID or API key are incorrect Please make sure your details are correct
    50001 Your company type does not allow receiving orders Please contact support
    50002 Your company has been deactivated
    50003 The IP address used to send the request does not appear in the IP white list
    50004 The rate limit has been exceeded

    Administrative Errors

    An example response for when an order is created in an error state

            "id": "...",
            "external_ref": "TEST001",
            "has_error": true,
            "error_message": "Failed to split order due to invalid supplier information",
            "items": [
                    "id": "...",

    In certain circumstances, the API might return a success response code but create an order that is in an error state.

    Such error states can be dealt with by non-technical staff and do not represent issues with how the API has been used.

    One example scenario could be if the API is unable to correctly determine the supplier of an order line due to a misconfigured product in CPP.

    When an administrative error occurs, the order fields has_error and error_message will be populated with true and an error message respectively. The order status will also be set to QC Query.

    Virtual Product Creator

    Before embedding the VPC tool into your website, you need the following pieces of information:

    Additionally, it is also necessary to specify two product category IDs:

    Please contact us for more details.

    Embedding the VPC

    $params = [
        'company_ref_id' => 99999,
        'approved_pc' => 1,
        'blanks_pc' => 2,
        'time' => time(),
        'nonce' => bin2hex(random_bytes(8)),
        'origin' => "{$_SERVER['REQUEST_SCHEME']}://{$_SERVER['HTTP_HOST']}",
    $query = http_build_query($params);
    $params += [
        'hmac' => hash_hmac('sha512', $query, $apiKey)
    $query = http_build_query($params);
    $iframeUrl = "{$query}";

    The VPC tool can be embedded into your site using an iframe set to the following URL:

    Several query parameters are also required:

    Name Description Required?
    company_ref_id Your unique company reference ID Yes
    approved_pc The ID of the CPP product category into which all created content is inserted Yes
    blanks_pc The ID of the CPP product category containing available product blanks Yes
    time The number of seconds since the UNIX epoch Yes
    nonce A random alphanumeric string that should be different each time you load the VPC Yes
    origin The origin of the page loading the VPC Yes
    hmac A HMAC message digest Yes

    Calculating the HMAC Message Digest

    Due to the nature of the VPC tool, it is necessary for the personalisation platform to be able to verify that your site is who it says it is. This is accomplished via the use of the HMAC algorithm.

    The input to the HMAC algorithm should be the query part of your iframe URL (minus the hmac parameter itself) whilst the HMAC secret key is your API key. sha512 should be selected as the hashing algorithm.

    The time query parameter is used to ensure that any HMAC value older than 15 seconds is rejected. This means that it is important that your server has the correct date and time set.

    For example, given the following query parameters:

    Name Value
    company_ref_id 99999
    approved_pc 1
    blanks_pc 2
    time 1543581543
    nonce randomstring

    the HMAC input data would be:


    which gives the below message digest when using AAAAAAAAAAAAAAAAAAAAAAAAAAA as the HMAC secret key:


    resulting in the final iframe URL:

    Product Creation Callback

    <!DOCTYPE html>
                html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; }
                iframe { width: 100%; height: 100%; border: 0; }
            window.addEventListener('message', function(e) {
                if( == 'PRODUCTS_CREATED' && e.source == document.getElementById('iframe').contentWindow) {
                    let products =;
                    // Do something to create the products within your own eCommerce
                    // platform
            <iframe id="iframe" src=""></iframe>

    Once the end user has created their products using the VPC tool, a PRODUCTS_CREATED message will be passed back to the parent site using the postMessage API.

    Your site can listen for such messages using Javascript.

    When the message is received, your site should use the data provided within the message to create relevant products in the most appropriate way for your particular eCommerce package.

    After creating the products, the VPC iframe should be closed and the user redirected to an appropriate landing page.

    The body field of the message will be an array containing an element for each product that the user selected.

    Each element has the following fields:

    Name Description
    id The CPP product ID that can be used in a Smartlink to reload the user's design
    description The design description entered by the user.
    name The design name entered by the user.
    retail_sku A unique SKU generated by the VPC tool using the product's supplier SKU as a base
    snapshots.small A 600x600 preview of the user's product
    base_product.productCode The SKU to use when sending future order lines for the product.

    For Suppliers


    This guide covers only some of the various ways in which suppliers who don't wish to use OMS can still integrate with the platform.

    Generic FileSystem Integration

    The Generic FileSystem integration is a simple way of receiving order details from the platform.

    Typically order details are represented by an XML file however we can support other formats on request.

    Files can be transferred to a supplier use several different protocols such as FTP, SFTP, FTPS and S3.

    Order API

    The order API (coming soon) allows suppliers to programmatically retrieve new orders from the platform via an API.

    Order Shipping API

    The order shipping API allows suppliers to make use of the platform's various shipping integrations to create shipments with providers such as Hermes and Royal Mail.

    When a shipment is created, a shipping label will be returned.

    Before Starting

    For both APIs, you will need the following information:

    Both will be provided to you by Custom Gateway. Please contact us for more details.

    Note on Code Samples

    The shell code samples to the right assume that you have access to the curl command line tool.

    Generic Filesystem

    Order Shipping API

    Before Starting

    Using the order shipping API requires a shipping integration to have been enabled on your relevant dropshipping accounts within the personalisation platform.

    Please contact us for more details.

    Shipping an Order

        -H "Authorization: Basic COMPANYREFID:APIKEY" -X POST

    A successful request will result in an array of shipments being returned:

            "id": 1234567890,
            "carrier": "hermes",
            "tracking": "ABCDEFGHIJKL",
            "label_url": "",
            "items": [
                    "id": 1234,
                    "order_id": 1234,
                    "quantity": 10,
                    "quantity_shipped": 10

    A order can be shipped by making a simple POST request to the order shipment endpoint.


    The shipping service used is automatically determined based on how your account (and possibly the retailers account) have been configured.

    A successful request will result in one or more shipment entities being created and returned.

    If an order has been split into multiple shipments, then the items collection on each shipment can be used to identify which items are included in that particular shipment.


    This section assumes a basic understanding of GraphQL and that the reader understands how to consume GraphQL APIs from their language of choice.

    More info on GraphQL can be found here


    $client = new \GuzzleHttp\Client;
    $clientId = ...;
    $clientSecret = ...;
    $scopes = [ ... ];
    $response = $client->post("", [
        "form_params" => [
            "client_id"     => $clientId,
            "client_secret" => $clientSecret,
            "grant_type"    => "client_credentials"
            "scope"         => implode(' ', $scopes)
    $json = (string)$response->getBody();
    $accessToken = json_decode($json, true)['access_token'];
    curl ""\
        -F "client_id=CLIENTID"\
        -F "client_secret=CLIENTSECRET"\
        -F "grant_type=client_credentials"\
        -F "scope=SCOPES"

    A successful request will return an access token.


    To use the GraphQL API, it is first necessary to obtain OAuth access tokens using your client ID and client secret.

    This can be done by making a POST request to the OAuth access token endpoint:

    On success, the request will result in a JSON encoded object that contains an access_token field.

    The access token is valid for 3600 seconds and must be provided via the Authorization header in subsequent API calls.

    Product Manager

    The product manager GraphQL API is located at:


    Example mutation

    mutation($baseProducts: [ID]!, $categoryId: ID!, $artwork: [CreateVirtualProductsFromArtworkArtwork]!) {
        createVirtualProductsFromArtwork(baseProducts: $baseProducts, categoryId: $categoryId, artwork: $artwork) {
    $client = new \GuzzleHttp\Client;
    $response = $client->post("", [
        "headers" => [
            "Authorization" => "Bearer {$accessToken}"
        "json" => [
            "query" => 'mutation($baseProducts: [ID]!, $categoryId: ID!, $artwork: [CreateVirtualProductsFromArtworkArtwork]!) {
                            createVirtualProductsFromArtwork(baseProducts: $baseProducts, categoryId: $categoryId, artwork: $artwork) {
            "variables" => [
                "baseProducts" => [ $productId1, $productId2, ... ],
                "categoryId" => $categoryId,
                "artwork" => [
                        "design_name": "Design #001",
                        "urls" => [
                                "preview" => "",
                                "original" => ""
    $json = (string)$response->getBody();
    $virtualProductIds = json_decode($json)['data']['createVirtualProductsFromArtwork'];

    Virtual products can be created via the createVirtualProductsFromArtwork GraphQL mutation, which accepts three mandatory variables.

    Required scopes:

    Variable Type Description
    baseProducts [ID]! A collection of base product IDs.
    categoryId ID! The ID of the target product category into which the new virtuals will be created.
    artwork CreateVirtualProductsFromArtworkArtwork A collection of artwork objects.

    Each artwork object has a:
    • design name
    • preview URL (used in live preview smartlinks)
    • full size URL (using for artwork generation)

    A new virtual product is created for each product specified via the baseProducts variable.

    Storing Images

    curl -X POST ""
    $client = new \GuzzleHttp\Client;
    $response = $client->post("", [
        'query' => [
            'url' => ''

    On success, a simple JSON response will be returned:

      "urls": {
        "original": "",
        "preview": "",
        "processed": ""

    It is possible to use Custom Gateway to host your images using the User Image API.

    The API is able to download an image from a URL, generate a live preview image automatically and store both.

    The URLs can then be used in the createVirtualProductsFromArtwork API call.