Skip to main content

Plugin Client Command Reference

Introduction​

The Plugin Client is used to initialize a Plugin, register handlers for messages the Plugin will receive, and communicate with the page hosting the Plugin (the "Plugin Host"). An instance of the Plugin Client is created using the initialize function documented below.

initialize​

Asynchronously sets up the Plugin Client instance to be used for the rest of the Plugin's execution. You only need to call initialize once to take care of all of the message handler registration.

PluginClient.initialize(messageHandlers)

A messageHandlers object must be provided to receive or respond to messages from the Host page. messageHandlers maps the names of handlers to handler functions, and the handler names themselves are defined by the Plugin Host. Each handler function will receive two parameters:

  • data: object - An object that contains the data sent from the Plugin Host -- If no data was sent, data will be null
  • pluginClient: PluginClient - The instance of the current PluginClient -- This allows the individual message handlers to access the various methods provided by the PluginClient, such as getContext

To respond to the Plugin Host's message, simply return the data to pass back:

const eventHandlers = {
helloWorld: function(data, pluginClient) {
console.log('Data from the Host page:', data)
console.log('The current PluginClient instance:', pluginClient)
return 'hello host!' // Send back the string 'hello world!' to the Host
},
}

Parameters​

  • messageHandlers: object<string, function> - An object containing all message handler functions

Return Value​

A Promise that resolves to an instance of the PluginClient. Any calls to initialize after the first will result in undefined behavior -- we recommend only initializing once.

Example​

The Plugin in this example handles creating and updating a bar chart visualization.

// an example made-up class for visualizing data
dataVisualizer = new DataVisualizer()

// a handler can be declared inline or outside of the messageHandlers object
const handleSetup = function(data, pluginClient) {
const width = data.width
const height = data.height
dataVisualizer.setDimensions(width, height) // sets the x,y dimensions of this data visualizer
}

// the dataVisualizer is passed into handleBarChartCreate through this binding later
const handleBarChartCreate = function(data, pluginClient) {
const chartData = data.chartPoints
const chartName = data.chartName
dataVisualizer.createBarChart(chartName, chartData)
}

const handleChartReset = function(data, pluginClient) {
dataVisualizer = new DataVisualizer()

const context = pluginClient.getContext()
const width = context.width
const height = context.height
dataVisualizer.setDimensions(width, height)
}

const eventHandlers = {
setup: handleSetup,
handleBarChartCreate: handleBarChartCreate,
handleChartReset: handleChartReset,
}

pluginClient.initialize(eventHandlers).then((pluginClient) => {
/* code that uses the client */
})

postMessage​

Sends a message to the Plugin Host page. Each Plugin Type will send and receive various messages unique to their type. That allow Plugins to interact with the XM Platform in novel ways. For information on messages available for a given Plugin Type check out the Plugin Guides.

pluginClient.postMessage(name, data, timeout)

Parameters​

  • name: string - The name of the message to send to the Plugin Host page. The Host page must have registered a handler for this message name to receive it, similar to how the Plugin registers handlers to receive messages from the Host page
  • data: serializable object (optional) - A serializable object to be sent along with the message. Defaults to undefined
  • timeout: number (optional) - Specifies the number of milliseconds after which to reject the returned Promise if no response has been received from the Plugin Host. Defaults to 10000 milliseconds (10 seconds)

Return Value​

Returns a Promise that will resolve with the Plugin Host's response, or reject if the Plugin Host throws an error. The Promise will also reject if the Host fails to respond before the timeout.

Example​

In this example, the plugin informs the host of a user activity within the plugin:

pluginClient.postMessage('elementClicked', {elementId: 'saveButton'})

In this example, the plugin is requesting chartData from the host and will wait for up to 2 seconds:

let chartData
pluginClient
.postMessage('getChartData', {chart: 'bar'}, 2000)
.then((response) => {
chartData = response.chartData
})
.catch((error) => {
// log error, etc.
})

fetch2​

Sends a request to the provided URL. Securely injects user defined account for authentication if specified in connection property. Works similarly to the vanilla JavaScript fetch function.

pluginClient.fetch2(url, fetchConfig)
info

HTTP Request Size: Please note that the combined size of the URL and payload in any inbound or outbound HTTP call is limited to 8 MB. You can chunk payloads over 8 MB into multiple calls to get around the limit.

Parameters​

  • url: string - The URL of the third party service to make a request to. Ex: https://graph.facebook.com/
  • fetchConfig: object - An object that configures the fetch request:
    • method: string - The HTTPS method to use. Ex: "POST", "GET", "PUT", etc.
    • headers: object<string, string> (optional) - Key/value pairs representing the request header(s). Ex: "Content-Type": "application/json"
    • body: string | object (optional) - The body of the request
    • params: object<string, string> (optional) - Key/value pairs representing the query parameter(s) to add to the request URL. Ex: "filter": "random" Would add ?filter=random to the URL.
    • connection: object (optional) Configures how a connection should be injected into the request:
      • connectionName: string - The name of the connection to use (use context.connections to get a list of available connection names and schemes)
      • paramFormat: string - Where to inject the credential. Can be "header","body", or "query"
      • paramName: string - What to name the credential parameter. As in, the field name for a body param, the query name for a query param, or the header name for a header. Ex: for a bearer token, this might be "Authorization"
      • paramTemplate: string - What to insert the credential as. Use it similarly to a string passed to String.format. Ex: "Bearer %s" would be a template for bearer tokens, "%s" inserts the token as-is
  • timeout: number (optional) Specifies the number of milliseconds after which to reject the returned Promise if no response has been received. Defaults to 10000 milliseconds (10 seconds)

Return Value​

Returns a Promise that resolves to the HTTPS response from the third party service or rejects on an error (such as the fetchConfig being improperly formatted), timeout, or non-2xx response code.

On a successful response from the third party service, the Promise resolves to an object with two properties:

  • status: number - The HTTP status code of the response from the the third party service. Will be a 2xx code, in this case.
  • responseData: unknown - The response data from the third party service. On a request timeout, internal service error, or any non-200 responses from the third party service, throws a FetchError with two properties:
  • status: number - The HTTP status code of the response from the the third party service. For programmatic detection of what went wrong.
  • responseData: unknown - Data that was returned in the response body; will likely contain additional details as to why the Error happened.

Interpolation​

Account information may be interpolated into the request at various points, using placeholders surrounded by double-braces, e.g. {{foo}}.

Inside the URL​

In the url parameter, placeholders referencing configuration attributes like {{foo}} are replaced with the user-provided value for that account configuration attribute. Assuming an attribute called foo that contains the value bar:

client.fetch2('https://{{foo}}.example.com/', {
method: 'GET',
connection: {
connectionName: 'example connection',
paramFormat: 'header',
paramName: 'Authorization',
paramTemplate: 'Bearer %s'
},
})

// calls https://bar.example.com/

An alternative to this method is to interpolate using user-provided values inside the account. An example of such a placeholder is

  • {{/credential/brandId}}: The brand ID value of the account, which we will set to be google.
client.fetch2('https://{{/credential/brandId}}.com/', {
method: 'GET',
connection: {
connectionName: 'example connection',
paramFormat: 'header',
paramName: 'Authorization',
paramTemplate: 'Bearer %s'
},
})

// calls https://google.com/

Note that both interpolation methods can be used simultaneously in a URL.

client.fetch2('https://{{foo}}.{{/credential/brandId}}.com/', {
method: 'GET',
connection: {
connectionName: 'example connection',
paramFormat: 'header',
paramName: 'Authorization',
paramTemplate: 'Bearer %s'
},
})

// calls https://bar.google.com/

Additionally, attempting to interpolate using a credential secret path like {{/credential/secret}} is disallowed for security reasons.

Inside the fetch config​

In strings inside the fetchConfig parameter, placeholders are replaced with the user-provided values inside the account. Valid placeholders include:

  • {{/credential/secret}}: The secret value of the account (e.g. for an API Key account, the API key)
  • {{/credential/metadata/foo}}: The value of the account metadata field foo
client.fetch2('https://example.com/', {
method: 'GET',
headers: {
auth: '{{/credential/secret}}',
},
params: {
q: '{{/credential/metadata/foo}}'
},
connection: {
connectionName: 'example connection',
paramFormat: 'header',
paramName: 'Authorization',
paramTemplate: 'Bearer %s'
},
})

// calls https://example.com/?q=bar with the header 'auth' set to the credential's secret

Placeholders may be used inside the headers, params, and body keys of the fetch config.

Limitations​

The payload size returning from a fetch2 request is limited to 8 MB.

Example​

Making a request to an external API at example.com:

// simple GET request
pluginClient
.fetch2('https://example.com/my/api/route', {method: 'GET'})
.then(function(response) {
// do stuff with response
console.log('status:', response.status)
console.log('data:', response.responseData)
})
.catch(function(error) {
// handle error
console.log('something went wrong!')
console.log('status:', error.status)
console.log('data:', error.responseData)
})

// get the first available connection name
const connectionName = pluginClient.getContext().connections[0].name

// fully configured POST request
pluginClient.fetch2('https://example.com/my/other/api/route', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
myParam: 'myValue',
},
query: {
filter: 'random',
},
connection: {
connectionName: connectionName,
paramFormat: 'header',
paramName: 'Authorization',
paramTemplate: 'Bearer %s'
},
})
// ignore any response by not having a .then()

fetch3​

Makes a request to the provided URL. Supports all content types including FormData for file uploads and binary data. Works similarly to the vanilla JavaScript fetch function and extends the features of our fetch2 API.

pluginClient.fetch3(url, fetchConfig, timeout)

Key differences from fetch2:

  • Supports all content types including FormData and binary data (images, PDFs, etc.)
  • Auto-decodes response data based on content-type (JSON->object, text->string, binary->ArrayBuffer)
  • Query parameters should be included directly in the URL, not in a separate params field
  • Automatic retry logic: If the destination server returns a 401/403 due to an expired credential, the request will automatically be retried with the refreshed credential

Parameters​

  • url: string - The URL of the third party service to make a request to. Include query parameters directly in the URL. Ex: https://api.example.com/data?filter=active
  • fetchConfig: object - An object that configures the fetch request:
    • method: string - The HTTPS method to use. Ex: "POST", "GET", "PUT", etc.
    • headers: object<string, string> (optional) - Key/value pairs representing the request header(s). Ex: "Content-Type": "application/json". Note: For FormData requests, do not set Content-Type manually as the browser will set it automatically with the correct boundary
    • body: string | object | array | FormData (optional) - The body of the request. Can be a string, object, array, or FormData (for file uploads)
    • connection: object (optional) Configures how a connection should be injected into the request:
      • connectionName: string - The name of the connection to use (use context.availableConnections to get a list of available connection names)
      • paramFormat: string - Where to inject the credential. Can be "header","body", or "query"
      • paramName: string - What to name the credential parameter. As in, the field name for a body param, the query name for a query param, or the header name for a header. Ex: for a bearer token, this might be "Authorization"
      • paramTemplate: string - What to insert the credential as. Use it similarly to a string passed to String.format. Ex: "Bearer %s" would be a template for bearer tokens, "%s" inserts the token as-is
    • checkBodyInjection: boolean (optional) - Whether to enable credential template injection in the request body. When enabled, you can use placeholders like {{/credential/field}} in your request body to inject credential metadata. Defaults to false
  • timeout: number (optional) Specifies the number of milliseconds after which to reject the returned Promise if no response has been received. Defaults to 10000 milliseconds (10 seconds)

Return Value​

Returns a Promise that resolves to the response from the third party service or rejects on an error (such as the fetchConfig being improperly formatted), timeout, or non-2xx response code.

On a successful response from the third party service, the Promise resolves to an object with properties:

  • data: unknown - The response data from the third party service, auto-decoded based on the Content-Type header: application/json -> parsed JavaScript object; text/* -> string; binary types (images, PDFs, etc.) -> ArrayBuffer
  • status: number - The HTTP status code of the response from the third party service. Will be a 2xx code, in this case
  • statusText: string - The HTTP status text of the response. Ex: "OK", "Created", etc.
  • headers: object<string, string> - The response headers from the third party service

On a request timeout, internal service error, or any non-2xx responses from the third party service, throws a FetchError with the same properties as above.

Requirements​

  • Plugin submitted with @qualtrics/plugin-client version 1.10.4 or greater.

Example​

Making a simple GET request:

pluginClient
.fetch3('https://api.example.com/users?status=active', {
method: 'GET',
})
.then(function(response) {
console.log('status:', response.status) // 200
console.log('statusText:', response.statusText) // "OK"
console.log('data:', response.data) // auto-decoded JSON object
console.log('content-type:', response.headers['content-type'])
})
.catch(function(error) {
console.error('Request failed:', error.message)
console.log('status:', error.status)
console.log('data:', error.data)
})

Making a POST request with authentication:

// get the first available connection name
const connectionName = pluginClient.getContext().availableConnections[0]

pluginClient
.fetch3('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
name: 'John Doe',
email: 'john@example.com',
},
connection: {
connectionName: connectionName,
paramFormat: 'header',
paramTemplate: 'Bearer %s',
paramName: 'Authorization',
},
})
.then(function(response) {
console.log('Created resource:', response.data)
})
.catch(function(error) {
console.error('Failed to create resource:', error)
})

Uploading a file using FormData:

// Create FormData with a file
const formData = new FormData()
formData.append('name', 'My Document')
formData.append('file', fileInput.files[0]) // File from an <input type="file">
formData.append('category', 'reports')

pluginClient
.fetch3('https://api.example.com/upload', {
method: 'POST',
body: formData,
// Do NOT set Content-Type header - browser will set it automatically with boundary
})
.then(function(response) {
console.log('File uploaded successfully:', response.data)
})
.catch(function(error) {
console.error('Upload failed:', error)
})

Downloading a binary file (PDF, image, etc.):

pluginClient
.fetch3('https://api.example.com/documents/123/download', {
method: 'GET',
})
.then(function(response) {
// response.data will be an ArrayBuffer for binary content
const blob = new Blob([response.data], {
type: response.headers['content-type'],
})
const url = URL.createObjectURL(blob)

// Create download link
const a = document.createElement('a')
a.href = url
a.download = 'document.pdf'
a.click()

URL.revokeObjectURL(url)
})
.catch(function(error) {
console.error('Download failed:', error)
})

Using credential template injection in the request body:

const connectionName = pluginClient.getContext().availableConnections[0]

pluginClient
.fetch3('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
apiKey: '{{/credential/apiKey}}', // Will be replaced with actual credential value
data: 'some data',
},
connection: {
connectionName: connectionName,
paramFormat: 'body',
paramTemplate: '%s',
paramName: 'apiKey',
},
checkBodyInjection: true, // Enable credential template injection
})
.then(function(response) {
console.log('Request succeeded:', response.data)
})

qualtricsApiFetch2​

Used for making requests to the Qualtrics Public API. Only a select few API endpoints are permitted for Plugin requests (listed below). This function was added as a substitute for qualtricsApiFetch. qualtricsApiFetch2 and has clearer Error messaging and a more detailed response object than the original qualtricsApiFetch.

pluginClient.qualtricsApiFetch2(url, config, timeout)

The entire Qualtrics Public API is available to your plugin if your XM Extension defines qualtricsScopes in the manifest.json. See Manifest.json Properties Reference for more information on how to add Qualtrics Scopes to your XM Extension. Brand Admins will be required to consent to the provided scopes before your XM Extension is available to use in customers' brands.

Parameters​

  • url: string - The relative URL path after /API/v3/. Ex: "whoami" will make a request to /API/v3/whoami
  • config: object - An object that configures the request:
    • method: string - The HTTPS method to use. Ex: "POST", "GET", "PUT", etc.
    • body: object | string (optional) - The body of the request
  • timeout: number (optional) - Specifies the number of milliseconds to wait before rejecting the returned Promise if no response has been received. Defaults to 10000 milliseconds (10 seconds).

Return Value​

Returns a Promise that resolves to the HTTPS response or rejects on an error (such as the config being improperly formatted), timeout, or non-2xx response code.

On a successful response from the Qualtrics API, the Promise resolves to an object with two properties:

  • status: number - The HTTP status code of the response. Will be 2xx in this case.
  • responseData: unknown - The response data from the Qualtrics API. See the Qualtrics API docs for what kind of values to expect to be returned by a successful call.

On a request timeout or any non-200 responses from the Qualtrics API, throws a FetchError with two properties:

  • status: number - The HTTP status code of the response for programmatic detection of what went wrong.
  • responseData: unknown - Data that was returned in the response body; will likely contain additional details as to why the Error happened.

Example​

// makes a request to GET /API/v3/surveys
pluginClient
.qualtricsApiFetch2('surveys', {method: 'GET'})
.then((res) => {
console.log('Got a successful request with stats code: ', res.status)
console.log('Surveys available to the current user: ', res.responseData)
})
.catch((err) => {
console.error('Something went wrong', err.responseData)
console.log('Here is the status code of the error', err.status)
})

getText​

Retrieves localized strings from the Plugin's translation files, specifically from the translation that matches the User's language. Valid keys are defined by top-level properties of the strings object in the translation file.

pluginClient.getText(key, placeHolders, defaultValue)

Parameters​

  • key: string - The key for the text template to use
  • placeHolders: object<string, string> (optional) - An object with values for any named placeholders in the template text
  • defaultValue: string (optional) - A default value to return if the key or translation doesn't exist

Return Value​

A string with the requested placeHolder values populated.

Example​

Inside the EN.json translation file for this Plugin:

{
"meta": {
"summary": "Facebook App Language Strings - EN"
},
"strings": {
"name": {
"text": "My Cool Facebook App",
"context": "The App Name"
},
"description": {
"text": "Does something cool with Facebook",
"context": "Displayed when listing the app"
},
"user.notifications": {
"text": "You have {{number}} notifications",
"context": "Displays when a user is checking how many notifications they have"
},
"comments.notification": {
"text": "{{user}} got a comment from {{name}}",
"context": "Displays when a user is checking a comment notification"
},
"duplicate": {
"text": "{{number}} is {{number}}",
"context": "Displays the same number twice at the bottom of the app UI"
}
}
}

Plugin code:

pluginClient.getText('name')
// My Cool Facebook App

pluginClient.getText('user.notifications', {number: 2})
// You have 2 notifications

pluginClient.getText('comments.notification', {user: 'Bob', name: 'Alice'})
// Bob got a comment from Alice

pluginClient.getText('duplicate', {number: 2})
// 2 is 2

// The following two will use the default, since comments.reaction doesn't exist in EN.json
pluginClient.getText('comments.reaction', {}, 'Thumbs Up')
// Thumbs Up

pluginClient.getText('comments.reaction', {number: 2}, '{{number}} Thumbs Up')
// 2 Thumbs Up

getContext​

Used to retrieve the context for the running plugin, provided by the Plugin Host page.

pluginClient.getContext()

Return Value​

An object containing the unique context data provided by the host page, as well as additional properties such as the user's language code, connections and the translation objects.

Example​

In this example, the host page provides a surveyId for the survey associated with this Plugin.

const context = client.getContext()
const surveyId = context.surveyId

getLanguage​

Returns the language code of the logged in user. Language codes follow the Qualtrics standard language codes.

pluginClient.getLanguage()

Return Value​

A string representing the language code of the logged in user. Can be used to locate the correct translation object in the context.

Example​

const languageCode = pluginClient.getLanguage()
console.log("User's chosen language: ", languageCode)

onCredentialStatusChange​

Passes a callback function to retrieve the credential status of a plugin. This is only relevant to plugin types that support rendering without a credential. Otherwise, the plugin can assume it has a valid credential available in its context.

pluginClient.onCredentialStatusChange(callback: (data: PluginCredentialData) => void): void

export type PluginCredentialData = {
isCredentialSelected: boolean
credentialStatus: CredentialStatus
connections?: {
name: string
scheme: string
}[]
}

export enum CredentialStatus {
INITIALIZED = 'initialized',
NEW = 'new',
DELETED = 'deleted',
UPDATED = 'updated',
}

Example​

In this example, the host triggers the callback when the credential is updated outside of the plugin UI. The plugin uses React to update the UI when the credential status changes.

const [
credentialStatusData,
setCredentialStatusData
] = React.useState<PluginCredentialData>();

React.useEffect(() => {
// initialize the callback on mount
props.client.onCredentialStatusChange(setCredentialStatusData);
}, []);

React.useEffect(() => {
// do something with the new credential status data
}, [ credentialStatusData ]);