Conga Product Documentation

Welcome to the new doc site. Some of your old bookmarks will no longer work. Please use the search bar to find your desired topic.

Show Page Sections

Performance Quoting customization

General presentation

Performance Quoting allows adding some custom logic to Quotes if necessary. This is possible by using an External Data source or by creating a custom process.

An interface is provided to integrate with CPQ Quotes in real-time, some example use cases are:

  • Pushing some Quote input to an external system to make some computation or retrieve some data.
  • Updating Quote data with a custom logic after a value change or any other trigger.

CUSTOMIZATION HOSTING

The customization code can be hosted in two ways:

  • Hosted by PROS: PLUGIN type
  • Hosted by user company in it's infrastructure: EXTERNAL type

Both modes offer nearly the same features, meaning that both can manage an External data source or a custom process (See definitions below).

The choice between hosting types will depend on the customization nature and workload.

Hosted by PROS

A customization hosted by PROS will help having an easier authentication and integration with other PROS applications.

  • Security will be mainly managed by PROS infrastructure and customization will be provided with the necessary information to interact with PROS APIs
  • Some issues can be detected by PROS and notified to the customization owner
  • Possible integration with a logging system available to customization owner
  • Resources are limited in number of requests, in allowed memory, and in processing duration (2 min)
    In case of PROS hosted customization, code must be developed by PROS integration teams:

Hosted by PROS customer

In case of a customization hosted by user company infrastructure:

  • Customization should be secured with an authentication, supported authentications are:
    • Basic authentication (not recommended)
    • Oauth2 with client credentials grant (recommended)
      • In this case, the PROS customer should have an OAuth2 authorization server
  • Code should manage authentication with needed PROS applications
    • PROS applications are secured using the PROS application portal token authentication
      • The customization code should use a service account to generate tokens to be able to interact with PROS APIs

        Code can be developed by PROS integration teams or by user company teams but validated by PROS teams for security purpose.

TYPES OF CUSTOMIZATION

External data source

An external data source is a customization type that allows pushing some input data from the Quote and receiving back output data to update directly the Quote.

Trigger

  • Explicitly when an end user (or an API) manually triggers a Refresh Datasource action related to the external data source
  • According to a Refresh Policy that can be configured on the columns mapping or fields mapping
    • None = no automated refresh
    • Once = called only once at line creation (quote creation for fields)
    • Each input = automatically depending on changes in the input parameters
    • when one of the input values of the map changes
    • or when one of the implicit/system inputs changes (a typical example of that is when a Pricing

      data source returns a price in EUR and the end-user changes the currency to USD)

    • Reload = when a quote session is launched (quote is re-opened or created)
    • Each input and reload = when one of the two previous events is raised.

      Tip

      Note: It's possible to select a date field/column to set the refresh date, it will be updated automatically each time the external data source is executed.

Input: The external data source input can be:

  • Inherited context keys/values, from the CRM for example
  • A static input with fixed keys/values as settings
  • Fields values from the Quote as settings
  • A data source mapping with either:
    • Field input mapping
    • Column input mapping

      Output:

It can be either:

  • Fields values
  • Cells values

    Behavior

    It's not allowed to call PROS Quoting APIs to get or update Quote data from a customization of type external data source.

    • If done you will access non up to date data and you can impact data consistency.
    • You should use custom process when needing to call Performance Quoting APIs.

      Other PROS platform APIs or external APIs can be called from an external data source.

  • The external data source can send back a direct custom notification in the response that will surface in the UI with a success or error message.
  • An external data source will call an API /getData that should be implemented on the customization code.

Custom process

A custom process allows the execution of the customization following a change on the Quote.

Trigger

  • Automatically: A field or a cell value change corresponding to configured fields or columns.
  • Explicitly: User triggers an action or type "Custom process"

Input: The custom process input can be:

  • A static input with fixed key value
  • A dynamic input from the Quote: The updated row ids when the custom process is triggered.

Output: The custom process cannot send back data in response, it just triggers an event.

Behavior:

  • The custom process will be executed in the same process than the trigger in a synchronous way to make sure having up to date data when calling Quote API an to better manage transactions.
    • An exception is made for some old customizations to keep compatibility. In this case execution of customization is asynchronous.
    • See customization implementation documentation for more details.
  • The custom process can call back Performance Quoting APIs to get or update some data. It can also call other PROS platform APIs or PROS customer APIs.
  • If you need to send a UI notification, a Performance Quoting API is available for that.
  • A custom Process will call an API /execute that should be implemented on the customization code.

Design of Quote customization

CUSTOM PROCESS DESIGN

Create a custom process

Create a custom process from the Data model menu in the Quote designer



Configure custom process general settings

  • Give a name to the custom process here UpdatePrices
  • Give a service name, this name will be used later to configure the customization URL and credentials
  • Add some static settings if necessary. Here Currency=USD for example.
  • The Post process execution check box is not available by default (only for some existing custom processes for compatibility reasons) and is unchecked by default:
    • The custom process will be executed in the triggering process.
    • This is the recommended and the default behavior.
    • If checked the custom process will be executed in a separate process than the trigger.

Configure triggers

In the triggers tab, you can configure fields and columns that will trigger the custom process following a value change.

  • Add one field or more if necessary
  • Add one column or more if necessary and select line templates within which the trigger should be used.

You can also use the Row creation and/or Row deletion as triggers for your custom process. Two columns are already available in the table and you just have to select line templates within which the trigger should be used.



Add a custom process action to the UI

The custom process can be triggered from a button on the UI.

For that you can create an action of type "custom process" as following:

  • Give an action name
  • Add permissions
  • Specify "custom process" type
  • Add the action to a toolbar or any allowed UI component

In the custom process action it's possible to add some settings:

  • A debounce time to avoid excessive clicks on the button
  • A check box to allow multiple selection
  • A check box to allow triggering the action with no selected line

EXTERNAL DATA SOURCE DESIGN

Create an external source

Create an external source from the Data model menu in the Quote designer



Configure external data source general settings

  • Give a name to the custom process here "externalService"
  • Give a service name, here "externalService", this name will be used later to configure the customization URL and credentials
  • Link one or more contexts to inherit from their mappings

  • Add some static settings if necessary. Here origin="QUOTE-X" for example.
  • Add a setting from a field value if necessary
  • A setting can be marked as mandatory so the customization can be sure to get it's value as input

Configure external data source column mapping

Select column mapping page to define input and output mappings:

  • Input mapping will send cell values as input for the customization
  • Output mapping will tell Performance Quoting about expected output from the customization
  • Select associated line templates
  • Define a refresh policy for triggering the customization, refresh policies are explained earlier in this document
  • If needed, select a refresh date column that will be automatically updated anytime the external data source is executed

    Note: It's not mandatory to setup an output if it's useful for the implemented use case

Configure external data source field mapping

Define a refresh policy for triggering the customization, refresh policies are explained earlier in this document

  • Input mapping will send field values as input for the customization
  • Output mapping will tell Performance Quoting about expected output from the customization
  • Define a refresh policy for triggering the customization, refresh policies are explained earlier in this document
  • If needed, select a refresh date field that will be automatically updated anytime the external data source is executed

    Note: It's not mandatory to setup any output if it corresponds to the implemented use case

Add a refresh action to the UI

In the user interface menu, you can add a new refresh action or select an existing one to associate an external data source.

When the action is executed, it will trigger the customization with configured input and output.



Performance Quoting Customization Development

PURPOSE

This section presents general guidelines for customization configuration and development. It's for implementation teams that need to setup and develop a customization.

For specific details depending on customization hosting type, please refer to related documentation.

DEVELOPMENT GENERAL GUIDELINES

The customization code should expose APIs (at least one) defined by the Open API contract

external-data-provider-definition-1.0.yaml

This API contract will be used also by performance Quoting (as an API client) to call one of these APIs:

  • /getData: In the case of an external data source
  • /execute: In case of a custom process

Please refer to this topic to have the latest APIs documentation and schema.

Servers implementing this API, must assume that JSON objects are open for extension. Server must not reject new fields that are not part of the Open API contract as it can evolve in the future.

This is consistent with the OpenAPI specification that is based on the JSON schema specification : Open API object definitions are considered open for extension by default, an additionalProperties directive is not required to make an object definition extensible. OpenAPI specification: https://swagger.io/specification/v2/

REQUESTS AND RESPONSE EXAMPLES

External data source

Example to retrieve quote fields:

From his external service, the user wants to get:

  • an dealAmountLast12Months
  • an dealDiscountLast12Months
  • a lastOrderDate

Based on:

  • customerName

The following JSON will be sent to the external service:

{
   "requestContext": {
       "tenantId": "1111-1111-1111-1111", // Tenant ID
       "environmentId": "dev", // Environment ID
       "userId": "user@quotex", //Current user ID
       "quoteId": "1111-1111-1111-1111", // Quote ID
       "requestId": "1111-1111-1111-1111", // Request ID
       "locale": "en_US" // Locale
   },
   "contextValues": [], // Values sent in the context
   "dpSettings": [], // Settings defined in the quote model
   "queryDefinition": {
       "select": [ //Values that have been to be retrieved
           {
               "key": "dealAmountLast12Months",
               "type": "Monetary"
           },
           {
               "key": "dealDiscountLast12Months",
               "type": "Monetary"
           },
           {
               "key": "lastOrderDate",
               "type": "DateTime"
           }
       ],
       "globalQueryElements": {
           "selectUnits": [
               {
                   "key": "dealAmountLast12Months",
                   "currency": "USD"
               },
               {
                   "key": "dealDiscountLast12Months",
                   "currency": "USD"
               }
           ],
           "criteria": [     
               {
                   "key": "customerName",
                   "value": {
                       "valueType": "String",
                       "value": "ABCCompany"
                   }
               }
           ]
       },
       "rowQueryElements": []
   }
}

The following JSON has to be sent to the quote (the order of the response list should be the same than the order of selects in the request):

{
   "errorMessage": "",
   "queryResult": {
       "globalValues": [
           { // dealAmountLast12Months
               "valueType": "Monetary",
               "value": 100344,
               "currencyCode": "USD"
           },
           { // dealDiscountLast12Months
               "valueType": "Monetary",
               "value": 9032,
               "currencyCode": "USD"
           },
           { // lastOrderDate
               "valueType": "DateTime",
               "value": 1571662152979
           }
       ],
       "multipleLinesvalues": []
   }
}

Example to retrieve lines data:

From his external service, the user wants to get:

  • an averagePriceLast12Months
  • an averageDiscountLast12Months
  • an averageVolumeLast12Months
  • a lastDate
  • a linked product

Based on:

  • product ID
  • color

The following JSON will be sent to the external service:

{
   "requestContext": {
       "tenantId": "1111-1111-1111-1111",
       "environmentId": "dev",
       "userId": "user@quotex",
       "quoteId": "1111-1111-1111-1111",
       "requestId": "1111-1111-1111-1111",
       "locale": "en_US"
   },
   "contextValues": [],
   "dpSettings": [],
   "queryDefinition": {
       "select": [
           {
               "key": "averagePriceLast12Months",
               "type": "Monetary"
           },
           {
               "key": "averageDiscountLast12Months",
               "type": "Monetary"
           },
           {
               "key": "averageVolumeLast12Months",
               "type": "BigDecimal"
           },
           {
               "key": "lastDate",
               "type": "DateTime"
           },
           {
               "key": "linkedProduct",
               "type": "Business"
           }
       ],
       "globalQueryElements": {
           "selectUnits": [
                   {
                       "key": "averagePriceLast12Months",
                       "currency": "USD"
                   },
                   {
                       "key": "averageDiscountLast12Months",
                       "currency": "USD"
                   }   
           ],
           "criteria": []
       },
       "rowQueryElements": [
           {
               "selectUnits": [
                     
               ],
               "criteria": [
                   {
                       "key": "color",
                       "value": {
                           "valueType": "String",
                           "value": "blue"
                       }
                   },
                   {
                       "key": "productId",
                       "value": {
                           "valueType": "String",
                           "value": "iPhone6"
                       }
                   }
               ]
           },
           {
               "selectUnits": [
                   {
                       "key": "averagePriceLast12Months",
                       "currency": "EUR"
                   },
                   {
                       "key": "averageDiscountLast12Months",
                       "currency": "EUR"
                   }
               ],
               "criteria": [
                   {
                       "key": "color",
                       "value": {
                           "valueType": "String",
                           "value": "green"
                       }
                   },
                   {
                       "key": "productId",
                       "value": {
                           "valueType": "String",
                           "value": "iPhone7"
                       }
                   }
               ]
           },
           {
               "selectUnits": [              
               ],
               "criteria": [
                   {
                       "key": "color",
                       "value": {
                           "valueType": "String",
                           "value": "red"
                       }
                   },
                   {
                       "key": "productId",
                       "value": {
                           "valueType": "String",
                           "value": "iPhone8"
                       }
                   }
               ]
           }
       ]
   }
}

The following JSON has to be sent to the quote (the order of the response corresponds to the order in the request:

{
   "errorMessage": "",
   "queryResult": {
       "globalValues": [],
       "multipleLinesvalues": [
           [
               {
                   "valueType": "Monetary",
                   "value": 100,
                   "currencyCode": "USD"
               },
               {
                   "valueType": "Monetary",
                   "value": 5,
                   "currencyCode": "USD"
               },
               {
                   "valueType": "BigDecimal",
                   "value": 5
               },
               {
                   "valueType": "DateTime",
                   "value": 1571662023929
               },
               {
                   "valueType": "Business",
                   "name": "iPhone6",
                   "type": "SI",
                   "namespace": "phone",
                   "description": "iPhone 6 64gb"
               }
           ],
           [
               {
                   "valueType": "Monetary",
                   "value": 200,
                   "currencyCode": "EUR"
               },
               {
                   "valueType": "Monetary",
                   "value": 10,
                   "currencyCode": "EUR"
               },
               {
                   "valueType": "BigDecimal",
                   "value": 5
               },
               {
                   "valueType": "DateTime",
                   "value": 1571662023929
               },
               {
                   "valueType": "Business",
                   "name": "iPhone7",
                   "type": "SI",
                   "namespace": "phone",
                   "description": "iPhone 7 128gb"
               }
           ],
           [
               {
                   "valueType": "Monetary",
                   "value": 300,
                   "currencyCode": "USD"
               },
               {
                   "valueType": "Monetary",
                   "value": 15,
                   "currencyCode": "USD"
               },
               {
                   "valueType": "BigDecimal",
                   "value": 5
               },
               {
                   "valueType": "DateTime",
                   "value": 1571662023929
               },
               {
                   "valueType": "Business",
                   "name": "iPhone8",
                   "type": "SI",
                   "namespace": "phone",
                   "description": "iPhone 6 252gb"
               }
           ]
       ]
   }
}

Error management

In the request sent to the quote, the errorMessage allows to display some messages to the UI.

{
   "errorMessage": "",
   "queryResult": {
       "globalValues": [],
       "multipleLinesvalues": [
...
}

If the HTTP request returns a value different from 200, a technical error is thrown at quote side with the message: "An error occurred in external service".

If the HTTP request returns 200 and a value is defined in the errorMessage section, the result of the request is ignored and the content of the errorMessage is displayed at runtime (the cell will be in error and the message will be displayed on the error picto).

Custom process

Request example supposing that the custom process will not call any other application or CPQ Quote.

The request may contain additional attributes depending on the hosting type. Please refer to documentation specific to the customization hosting type.

{
               "requestContext": {
                              "tenantId": "1111-1111-1111-1111", //Tenant ID
                              "environmentId": "dev", //Environment ID
                              "userId": "user@quotex", //current User ID
                              "sessionId": "1111-1111-1111-1111", //Session ID
                              "quoteId": "1111-1111-1111-1111", //Quote ID
                              "requestId": "1111-1111-1111-1111" //Request ID
               }
               "dpSettings": [{    //Example of a static setting defined on the custom process entity
                              "key": "staticSetting",
                              "value": "my setting"
               }],
               "rowIdentifiers": ["1", "2", "3"] //list of Row Ids of the selected lines
}

Testing customization code

Testing API in Postman

This is a required step to ensure customization is compliant with the API contract.

Download the customization API spec from this link, extract external-data-provider-definition-1.0.yaml file from this zip file.

Import the yaml to Postman as a Collection



Request for External Data Source



Response for External Data Source



Request for Custom Process



PUBLIC API USAGE FROM CUSTOMIZATION CODE

Quoting Public API documentation is available at this link.

Public APIs general guidelines should be respected, for example governor limits, timeout and error management, polling API usage, etc.

Only relevant APIs for a customization are allowed to be called otherwise you can have a 403 response in case of a forbidden API:

  • For example, it has no sens to call OpenQuote or cleanSession APIs from a customization as it's already under use.

Only APIs authorized by the calling service account role are allowed. API_USER role is relevant for most of customization use cases.

ADVANCED FEATURES

Context propagation

When iterating with Performance Quoting application, either from Public APIs or from the UI, initial requests are associated to some context, it contains information like:

  • User Id
  • Batch Id in case of batch requests to the API
  • System mode authorized for some roles and some use cases
  • Any other internal context information

When calling a customization from performance Quoting, we should keep this context when calling back Quoting APIs otherwise it could cause some issues:

  • Loosing original access rights
  • Rate limiter will consider all calls from the customization as issued by one user, API limits will be reached very quickly
  • In some cases customization code can cause many unwanted loops, these loops can be avoided with propagation context.

The context propagation is enabled by default, however it's disabled for customers having already at least one customization to keep backward compatibility.

If you need the feature for an existing customization, please ask for it to be enabled by creating a request.

We recommend using the context propagation to avoid issues with Quote Public API rate limiting at least.

When feature is enabled, the customization will receive extra information in the request to provide when calling Quote APIs

The customization should add one on more headers to the request to public API with provided header name and header value.

See details on how to implement context propagation depending on the hosting type in the corresponding detailed implementation page.

In process execution mode

The "In process execution" mode is available only on custom process it allows the execution of the custom process inside the triggering process. It's the recommended and default behavior.

This feature might not be enabled for some existing customizations for compatibility reasons (When the "Post process execution" checkbox is enabled in the Quote designer).

We recommend migrating to "In process execution" mode mode as it offers following advantages:

  • The custom code is transactional with the change which triggered it. When reading quote contents, the values reflect the contents after the change is performed.
    • If the customization fails, then the change which triggered it also fails and no change is performed at all. It won't leave an inconsistent state.
      • This is also true if a customization triggers another customization (loop).
  • Changes to the quote can be done directly from the customization. No need for "async" execution (which cannot guarantee if we read quote contents before/after the change is performed).
  • Guarantees the same customization is not executed multiple times for the same change.
  • Avoid infinite loop issues (a customization makes a change which triggers a customization which makes a change which triggers...)
  • No misuse of API: only relevant APIs are accessible through "In process execution" mode.

Performance Quoting Customization: Customization Hosted by PROS customer

PURPOSE

This section presents guidelines for customization configuration and development of customization hosted by PROS customer.

It's scoped for implementation teams that need to setup and develop a customization hosted outside of PROS network by the customization owner in it's own infrastructure.

For development guidelines of PROS hosted customization please refer to the specific documentation.

EXTERNAL DATA SOURCE AND CUSTOM PROCESS CONFIGURATION

Create Service Account for performance Quoting API

Implementation teams will be responsible for creating and storing a service account to authenticate with Quoting APIs or any other PROS platform APIs.

  • For Quoting APIs: Use API_USER role as it's sufficient for most common use cases.

Follow this Connect link to create a service account for Quote API, application is QuoteX, and user role is API_USER

Follow this instruction to convert private key from pem to PKCS8 format. This is required for configuration.

Create a request to provision External Data source or Custom Process

A request to CPQ Cloud team should be created to declare a new external service. The request

should include:

  • Tenant id/ Tenant environment Id.
  • Type of the customization: EXTERNAL In case this case
  • Service name that is linked to the data source or custom process as configured in the Quote Designer.

    External server authentication credentials should be also provided.

    • See OAuth2 documentation bellow, this is the recommended authentication for customizations
    • Basic authentication is also supported

OAUTH2 AUTHENTICATION IMPLEMENTATION

PROS Smart CPQ supports Oauth2 authentication for external data sources and custom process based on RFC6749 specification.

The following will explain:

  • The authentication scheme
  • Define authentication specification and prerequisites

Authentication scheme

PROS Smart CPQ supports Oauth2 authentication with "client_credentials" grant type for external data sources. The authentication workflow is described as follows:



  • PROS Smart CPQ will first request an access token from the authorization server. The access token will be kept and reused until it expires.
  • All requests to the external servcie API will contain a header with the access token.
  • The authorization server and the external service must support HTTPS protocol to secure requests content.

Specifications

Token request:

PROS Smart CPQ sends an authorization request (POST) with "url encoded" content type. The request contains following parameters

  • client_id
  • client_secret
  • grant_type (PROS Smart CPQ supports only client_credentials grant type)

Here are possible request formats:

  • HEADER: sends credentials in a basic authentication header
  • BODY: sends credentials in the POST body

The choice between option 1 or option 2 depends on provided configuration (See "Configuration" section)

Example with option 1:

curl -X POST -H "Accept: application/json" -H "Content-type: application/x-www-form-urlencoded" -H "Authorization: Basic aXQtdGVzdC1jbGllbnQtY3JlZGVudGlhbC1ncmFudDpzb21lLXNlY3JldC1jbGllbnQtY3JlZGVudGlhbC1n cmFudA=="

-d 'grant_type=client_credentials' https://localhost:8080/oauth2/token

Example with option 2:

curl -X POST -H "Accept: application/json" -H "Content-type: application/x-www-form-urlencoded" -d 'grant_type=client_credentials&client_id=TestClient&client_secret=TestSecret' https://localhost:808 0/oauth2/token

Expected token response:

Success response

The token response must contain at least following attributes:

  • access_token
  • token_type
  • expires_in

Any other attribute will be ignored.

The expires_in attribute will be used to cache the access token and thus reduce access token requests.

Example:

{ "scope": "read", "access_token": "38b0b9e5-0ff0-42f9-a9df-28cfaf996de2", "token_type": "Bearer", "expires_in": 0 }

Error response

Error responses must be returned with an HTTP 400 status code, with error and error_description attributes.

The error attribute can be one of the values listed below.

  • invalid_request – The request is missing a parameter so the server can’t proceed with the request. This may also be returned if the request includes an unsupported parameter or repeats a parameter.
  • invalid_client – Client authentication failed, such as if the request contains an invalid client ID or secret. Send an HTTP 401 response in this case.
  • invalid_grant – The authorization code is invalid or expired. This is also the error you would return if the redirect URL given in the authorization grant does not match the URL provided in this access token request.
  • invalid_scope – For access token requests that include a scope, this error indicates an invalid scope value in the request.
  • unauthorized_client – This client is not authorized to use the requested grant type.

    Example:

{ "error": "invalid_request", "error_description": "The error message" }

API request with Oauth2 access token

All requests to the external service will contain a header "Authorization" containing the access token with the token type.

Example:

Supposing that the "token_type" is "Bearer" a request to a external service API could be:

curl -X POST -H "Accept: application/json" -H

"Authorization: Bearer aXQtdGVzdC1jbGllbnQtY3JlZGVudGlhbC1ncmFudDpzb21lLXNlY3Jld C1jbGllbnQtY3JlZGVudGlhbC1ncmFudA==" https://localhost:8080/service/api1

Configuration

Here are required information that you need to provide in order configure Oauth2 authentication with Performance Quoting customization calls.

CONFIGURATIONDESCRIPTION
Service nameId of the service in order to identify it in the quote model (data provider name)
Service End pointThe service end point URL. It's simply the URL where to send API requests to the data provider
Access token URLThe authorization server URL. It's the URL to use for access token requests
Client IdThe identifier for PROS Smart CPQ on your authorization server
Client secretA strong secret key for PROS Smart CPQ
Client credentials typeDefines the way to send access token request (HEADER or BODY). See token request specification above
scopesList of scopes to use

CONTEXT PROPAGATION IMPLEMENTATION

In case of a customization hosted by customer (EXTERNAL type), customization will receive many header information to forward in the "externalApplicationContext" attribute in "headers" table.

The customization code should create an send an HTTP header when calling Quoting public API for each element in the request attribute "headers"

For the example request bellow the customization should add two headers when requesting Quote public API like:

AuthContext: "0:EncryptedHeaderContent"

Request-Id: "963ba3e7359143adbddeee391fad35b0"

Please note that headers names and values may change (AuthContext, Request-Id are not a fixed value and may change at any time):

{
   requestContext: {
     tenantId: 'a8515c30-42cc-11e6-beb8-9e71128cae77',
     environmentId: 'a8515c30-42cc-11e6-beb8-9e71128cae77',
     userId: '[email protected]',
     sessionId: '44cfd0b0-caff-46ff-8bdd-2b237a2fa189',
     quoteId: 'bed61232-7bab-4ad4-88a5-875f94eba08d',
     quoteModelId: 'QM-148be401b31c4cd9b4a91bf4a0eb05de',
     requestId: '|ba8e4101ddab4a0fb7b0b968a8bb06ac.fc4d5f_89b41a_',
     locale: 'en_US'
   },
   externalApplicationContext: {
     appType: 'QUOTEX',
     headers: [
       {
         name: 'AuthContext',
         value: '0:EncryptedHeaderContent'
       },
       {
         name: 'Request-Id',
         value: '963ba3e7359143adbddeee391fad35b0'
       } 
     ]
   },
   dpSettings: [ { key: 'origin', value: 'QUOTE-X' } ],
   rowIdentifiers: [ '1' ]
 }