Using the Apps framework
Use the ZAF SDK v2 to interact with the Apps framework directly from your iframe. The SDK provides a ZAFClient
global object that allows cross-frame communication between your app and the host Zendesk product. For details, see the ZAF Client API doc.
Getting the SDK
Import the ZAF SDK from https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js
. Example:
<script type="text/javascript" src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>
Using the URL in your app ensures the SDK is automatically updated with the latest 2.0 patch releases, including any bug fixes. For more information, see Framework Versions.
Working with framework APIs
The get
, set
and invoke
methods of a ZAFClient
object provide an interface between your iframe and the framework APIs. Due to the nature of cross-frame communication, every interaction between your iframe and the framework happens asynchronously. get
, set
and invoke
return a JavaScript Promise object with the result of the call.
get
var client = ZAFClient.init();
client.get('ticket.requester.name').then(function(data) {
console.log(data); // { "ticket.requester.name": "Mikkel Svane" }
});
set
var client = ZAFClient.init();
client.set('ticket.type', 'task');
invoke
var client = ZAFClient.init();
client.invoke('comment.appendText', 'My printer is on fire');
Bulk calls
The get
, set
and invoke
methods of ZAFClient
support bulk calls.
Bulk get
var client = ZAFClient.init();
client.get(['ticket.subject', 'ticket.requester.name']).then(function(data) {
console.log(data); // { 'ticket.subject': 'Help, my printer is on fire', 'ticket.requester.name': 'Mikkel Svane' }
});
Bulk set
var client = ZAFClient.init();
client.set({ 'ticket.subject': 'Printer Overheating Incident', 'ticket.type': 'incident' }).then(function(data) {
console.log(data); // { 'ticket.subject': 'Printer Overheating Incident', 'ticket.type': 'incident' }
});
Bulk invoke
var client = ZAFClient.init();
client.invoke({
'ticket.comment.appendText': ['My printer is on fire'],
'ticketFields:priority.hide': []
});
Error handling
If an error occurs when calling get
, set
or invoke
with a single path, the promise
is rejected with an error. Errors can occur in case the path is invalid or given an invalid argument.
Example:
// invalid path
client.get('nonExistentPath').then(function(data) {
console.log(data); // never run
}).catch(function(error) {
console.log(error.toString()); // "APIUnavailable: "nonExistentPath" Could not find handler for: "nonExistentPath"
});
// invalid argument
client.set('ticket.form.id', -1).then(function(data) {
console.log(data); // never run
}).catch(function(error) {
console.log(error.toString()); // Error: "ticket.form.id" Invalid Ticket Form ID
});
When making bulk calls, the promise
is always resolved. However, if one of the paths is invalid or if the call is given invalid arguments, the framework includes an errors
property with the failed paths as keys and their error objects as values.
Example:
// invalid path
client.get(['ticket.subject', 'nonExistentPath']).then(function(data) {
console.log(data);
/*
{
'ticket.subject': 'Help, my printer is on fire',
'errors': {
'nonExistentPath': Error("Could not find handler for: 'nonExistentPath'")
}
}
*/
});
// invalid argument
client.set({
'ticket.subject': 'Printer Overheating Incident',
'ticket.form.id': -1
}).then(function(data) {
console.log(data);
/*
{
'ticket.subject': 'Help, my printer is on fire',
'errors': {
'ticket.form.id': Error("Invalid Ticket Form ID")
}
}
*/
});
Additional resources
For a complete list of available APIs, see the following docs:
Working with framework events
Use the on
method of a ZAFClient
object to listen for events. For available events, see the listings by location in the following docs:
Example:
var client = ZAFClient.init();
client.on('ticket.updated', function() {
handleTicketUpdated();
});
Hook Events
Hook events allow your app to hook into product events and block the completion of the event until your app responds.
If your event handler returns a promise, the UI will wait until the promise resolves. Alternatively the event handler can abort the user request by returning false
or a string to show as an error message. In the following example, the handler for the ticket.save
hook event prevents the user from saving the ticket:
var client = ZAFClient.init();
client.on('ticket.save', function() {
return false;
});
You can register multiple hook events. However, for the event to continue processing (for example, for the ticket save to be committed), all promises returned by the event handlers must be successfully resolved. If any event handler returns a rejected promise, throws an exception, or returns false
or a string, the event is aborted. If a string is returned directly or passed with the promise rejection, the string is displayed as an error message in the UI.
Example:
var client = ZAFClient.init();
client.on('ticket.save', function() {
return client.get('ticket.comment.text').then(function(data) {
return fetch('https://myapi.example.org/is_polite?comment=' + data['ticket.comment.text']).catch(function() {
throw 'You must be more polite';
});
});
});
Hook events have a 30-second timeout period during which your app must respond. Otherwise the event will be aborted by default.
Working with requests
You can make network requests to external servers using whatever JavaScript functions you like, including native XMLHttpRequest
and fetch
methods in modern browsers.
You can also use the App framework's ZAFClient object to create a client object and then use the client's request()
method. Use the request()
method only if you need to make requests that are:
- To the REST API for the product running your app (see below), or
- Proxied due to cross-domain restrictions, or
- Proxied to interpolate secure settings, or
- Proxied to interpolate OAuth access tokens
Example:
var fetchSelf = {
url: '/api/v2/users/me.json',
type: 'GET',
dataType: 'json'
};
var client = ZAFClient.init();
client.request(fetchSelf).then(function(data) {
console.log(data);
});
Note: The client.request()
method doesn't support uploading and downloading binary files. A workaround is to use JavaScript or jQuery AJAX calls.
Making native REST API requests
You can use the ZAF client's request()
method to make HTTP requests to the Zendesk Support and Sell REST APIs. Requests to the Chat REST API are not supported yet, though of course requests to the Chat JavaScript API are.
Support requests
To make requests to the Support REST API, look up the endpoint in the Support REST API docs and use the following truncated path for the url
property:
api/v2/{rest-of-path}
Example:
zafClient.request({
url: '/api/v2/tickets.json',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
"ticket": {
"subject": "Test ticket #1",
"comment": { "body": "This is a test ticket" }
}
})
})
Sell requests
To make requests to the Sell REST API, look up the endpoint in the Sell REST API docs and use the following truncated path for url
:
/v2/{rest-of-path}
Note: Only the Sell Core API is supported. Other Sell APIs such as the Search API and Firehose API are not supported.
Example:
zafClient.request({
url: '/v2/deals',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
"data": {
"name": "Website Redesign",
"contact_id": 1,
}
})
})
Rate limits
Requests from an app to the Zendesk API are throttled at 100 requests per minute per app user. In addition to the app user rate limit, the Zendesk API also has an account rate limit depending on your plan. If a request is rate-limited by the account limit, the framework will auto-retry the request after a delay. To disable the retry behavior and handle rate-limiting yourself, set the autoRetry
option to false
in your request options. For more information about the account rate limits, see Rate limits in the Zendesk REST API docs.
If you're rate limited by the account limit, the relevant endpoint returns a 429 response. If you're rate limited by the app user limit, you should see a browser console message.
Furthermore, if the account limit is hit, the response will take the following form:
Status: 429 Too Many Requests
{
"errors": [
{
"code": "TooManyProxiedRequests",
"status": "429",
"title": "Too Many Requests",
"message": "You have exceeded the maximum number of secure requests per minute for this account."
}
]
}
To summarize:
- A certain number of requests / minute / account depending on your plan
- 100 requests / minute / app user
Examples:
- A customer on an Enterprise account using one of your apps has 10 agents on average online at any one time. In a particularly busy period, each agent makes in excess of 70 requests per minute using your app. They are not limited by the app user limit, but the server blocks some requests because they collectively exceeded the account rate limit of 700 requests per minute for the Enterprise subscription level.
- An agent makes 15 requests in one minute with an app, along with 20 other apps that cumulatively make 150 requests in the same minute, with no app making more than 100 requests. Since the app user limit hasn't been exceeded for any individual app, the requests aren't rate limited.
- An agent has 5 ticket tabs open with your app. Each instance of your app installation in the said location makes 25 requests in the same minute. Because the agent made 125 requests with your app, 25 of these requests are throttled and will be attempted again after some time has elapsed.
- An agent has a single ticket tab open with your app. In the top bar location, the app makes 20 requests. In the sidebar location, the app makes 30 requests. In the nav bar location, the app makes a further 30 requests. Finally, in the background location, the app makes a further 60 requests. Since these requests belong to the same installation, your app will be rate limited for the agent.
Using secure settings
When an app makes AJAX requests, the settings for the request can be viewed in the browser console. Some settings may contain sensitive information such as an API key or token. Secure settings are a way to make settings inaccessible to agents when making AJAX requests. The setting values are only inserted in the outbound request server-side at the proxy layer.
Secure settings only work in requests made with the framework's client.request()
method.
Limitations
Secure settings don't work with CORS requests made with the
client.request()
method. The outbound CORS requests bypass the proxy layer, so the secure setting can never be inserted in the request. See Making CORS requests.Secure settings don't work if you're testing the app locally using the ZAT server. However, you can install the app as a private app and then use ZAT to make updates to the installed app. You can also run the app locally after installing the app remotely. See Testing an app with secure settings locally.
client.request
filters out any secure setting values that are echoed back by the server for security purposes.Secure settings field value must not be more than 1024 characters.
Secure settings can not be updated to be insecure. Once a secure setting is created, it can only be deleted in manifest updates.
Regular settings can not be updated to be be secure. They must be re-created with the
"secure"
property set totrue
.
Server requirements
To successfully make secure requests, the responding server must:
- Allow requests from the IP address ranges documented in this article.
- Provide a valid and complete SSL certificate chain. For example,
curl https://YOUR-SERVER/
won't fail with SSL errors using a standard set of root certificates such as the Mozilla/Debian bundle published in the Ubuntu packages.
Example
Secure settings are configured in your manifest file. To secure a setting, add a "secure": true
property to the parameter that defines your setting. Example:
"parameters": [
{
"name": "token",
"type": "text",
"secure": true
},
...
]
You also need to define a domainWhitelist
property in the manifest file. Only requests to the white-listed domains will work for secure requests. The whitelist prevents attackers from hijacking the secure setting to reroute it to another domain and using it. Non-secure requests aren't affected by the whitelist.
{
...
"domainWhitelist": ["example.com"],
"parameters": [
{
"name": "token",
"type": "text",
"secure": true
},
...
]
}
Specify only the domain name or names in the array. Don't include "https://". You can also configure the property on a per-installation basis using a regular installation setting. Example:
"domainWhitelist": ["www.teachmyapi.com", "{{setting.subdomain}}.herokuapp.com"],
To secure your HTTP request, replace the sensitive information in the code that defines the request with a {{setting.name}}
placeholder, and add a secure: true
property. Example:
var settings = {
url: 'https://www.example.com/api/v2/leads.json',
headers: {"Authorization": "Bearer {{setting.token}}"},
secure: true,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({lead: {name: 'Jay Gatsby', email: '[email protected]'}})
};
var client = ZAFClient.init();
client.request(settings).then(...);
When the secure settings are saved, the app interface displays "blank" for the setting values that are secure.
In the example, the bearer token is hidden from agents. All they can see in the console is the placeholder. The token is inserted server-side at the proxy layer.
When the admin installs the app for the first time, they'll be prompted to enter the token value:
Zendesk Support stores the value and inserts it in outgoing requests.
Note: For security reasons, secure app setting values are not redisplayed once entered. The secure setting input fields will appear empty in the app configuration dialog. However, previously saved values are retained until overwritten by new values.
Note: The double-curly-bracket syntax {{setting.token}}
works for inserting a secure setting in a request. You can't use the syntax to insert a secure setting elsewhere in your JavaScript code. Use the framework's client.metadata()
method to retrieve and insert non-secure settings in your code. See Defining installation settings.
Using OAuth
You can make requests to third-party products using OAuth with the App framework's client.request()
method. Include a secure: true
property in the argument object.
Start by specifying your OAuth details in the oauth
parameter in your manifest file. See oauth in the manifest reference. Example:
"oauth": {
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"authorize_uri": "https://example.com/oauth",
"access_token_uri": "https://example.com/oauth/access_token",
"scope": "read write"
}
You also need to add a parameter of type "oauth" to the parameters
list:
"parameters": [
{
"name": "token",
"type": "oauth"
}
]
In your app code, use the placeholder {{setting.token}}
and a secure: true
property to make an OAuth request.
var settings = {
url: 'https://www.example.com/api/user',
headers: {"Authorization": "Bearer {{setting.token}}"},
secure: true,
type: 'GET'
};
var client = ZAFClient.init();
client.request(settings).then(...);
Similarly to secure settings, the OAuth access token is inserted server-side at the proxy layer.
OAuth client redirect URL
In your OAuth client settings, set the Redirect URL to:
https://zis.zendesk.com/api/services/zis/connections/oauth/callback
Using parameters returned from OAuth authentication
If you need any parameter returned from the OAuth authentication, add a corresponding parameter of type "hidden" to the parameters
list in your manifest file and prefix the parameter name with "oauth_".
Example:
Suppose the OAuth authentication returns the following parameters:
state=1&shop=myshop
You can save and access the 'shop' parameter by adding a hidden parameter named 'oauth_subdomain' to your manifest file.
"parameters": [
{
"name": "token",
"type": "oauth"
},
{
"name": "oauth_shop",
"type": "hidden"
}
]
You can then access the returned data in your code like a normal setting using client.metadata()
.
Authorization code grant flow
The Apps framework only supports the authorization code grant flow. The flow has the following steps:
- When the app is installed, the user is prompted to set up OAuth.
- The OAuth server prompts the user to provide a username and password.
- If the credentials are correct, the authorization server redirects the browser back to the
redirect_uri
which contains a generated authorization token as a parameter. - The app makes a POST request to the service’s token endpoint
access_token_uri
, which includes theclient_id
,client_secret
, andauthorization_token
. An access token is returned, which is used for API calls. - Storing and refreshing access tokens is managed by the Zendesk token service.
Zendesk token service
The Zendesk token service provides OAuth token storage. When a request is made, the token can be automatically refreshed by the Apps framework if the token contains the expiry_date
and refresh_token
parameters.
Refreshing tokens for systems managing refreshed tokens in a different manner (for example, Salesforce tokens using OAuth 2.0 Token Introspection) is not currently supported.
Making CORS requests
You can make cross-origin HTTP requests for external resources using whatever JavaScript functions you like, including the native XMLHttpRequest
and fetch
methods in JavaScript.
If you use the App framework's client.request()
method, include a cors: true
property in the argument object:
var fetchItem = {
url: 'https://cdn.example.com/items/30',
cors: true,
type: 'GET',
dataType: 'json'
};
var client = ZAFClient.init();
client.request(fetchItem).then(function(data) {
console.log(data);
});
Encoding and sending JSON web tokens
The App framework's client.request()
method supports encoding and sending JSON web tokens (JWT). JWT is a way of encoding and sending information in an HTTP request to be verified by a second party. It's often used for single sign-on authentication. For details, see Anatomy of a JWT request in the Support Help Center.
The information in JWT tokens is not encrypted. It's only digitally signed with the specified secret key. As a result, include only information that's not secret but needs to be verified by the receiving party, such as the names of Zendesk Support users. Don't include any sensitive information such as passwords.
To encode and send a JWT token:
- Define a
jwt
object in the options object that configures your request. The options must also includesecure:true
andalgorithm: 'HS256'
. HS256 is the only signing algorithm available for{{jwt.token}}
values. - Use the
{{jwt.token}}
placeholder in the same options object, such as in the request url or in an authorization header. - Pass the options object to the
request()
method.
Example:
var options = {
url: 'https://www.example.com/app',
headers: {
'Authorization': 'JWT {{jwt.token}}'
},
contentType: 'application/json',
secure: true,
jwt: {
algorithm: 'HS256',
secret_key: 'some_shared_secret',
expiry: 3600,
claims: {
iat: 1372113305,
jti: '8883362531196.326',
iss: 'some_subdomain',
}
},
type: 'POST',
dataType: 'json'
};
var client = ZAFClient.init();
client.request(options).then(function(data) {
console.log(data);
});
You can use secure settings to prevent sensitive data in the token from being accessed by agents in the browser console. Example:
jwt: {
secret_key: '{{setting.shared_secret}}',
claims: {
...
}
}
Only Latin-1 (ISO-8859-1) characters are allowed in the token. Non-UTF-8 headers will still be parsed as UTF-8.
A Zendesk proxy server will intercept the outgoing request and replace the {{jwt.token}}
placeholder with the encoded token.
Note: The placeholder doesn't work with CORS requests made with the client.request()
method because those requests bypass the proxy layer.
Authenticating Zendesk in your server-side app
A Zendesk app can consist of a web application running on a remote server that generates and serves all the HTML loaded in an iframe in a Zendesk product. No application logic or content is stored in the Zendesk infrastructure. When the app opens in the product, Zendesk must request the initial page from the server-side app. Subsequent page requests to the server usually originate from the iframed app itself.
If you're building a server-side app, one security feature to consider is verifying that an HTTP request for the initial page originates from a legitimate Zendesk product instance.
To help you, Zendesk can include a JSON Web Token (JWT) in the request for the initial page. After receiving the request, your server-side app can check the signed token to validate that the request originated from a legitimate Zendesk product instance. This helps prevent downgrade attacks.
The signed token also contains a number of attributes (known as claims) that your server-side app can use to look up externally stored values associated with your Zendesk Support account.
Zendesk includes the JWT token only in requests for the initial page of the app. This page is specified in the location
object in the app's manifest.json file. Example:
"location": { "support": { "ticket_sidebar": { "signed": true, "url": "https://myapp.example.org/" } } }
This feature is only available for apps that specify a page on a remote server in the location
object. It's not available for apps that specify a page hosted on the Zendesk infrastructure, such as "assets/iframe.html"
.
Enabling the JWT token in Zendesk
To get Zendesk to include a JWT token in its request for the initial app page, include one of the following properties in your app's manifest.json file:
To include a JWT token for the app in all locations, add
"signedUrls": true
to the top-level manifest object. Example:{ "name": "My App", "signedUrls": true, "location": { ... }, ... }
or
To include a JWT token for the app only in a specific location, add
"signed": true
to the named location in thelocation
object. Example:"name": "My App", "location": { "support": { "ticket_sidebar": { "signed": true, "url": "https://myapp.example.org/" } } },
Handling the JWT token in your server-side app
Once the JWT token is enabled, Zendesk does the following:
- Changes the request method for the initial page from GET to POST
- Includes the JWT token in a field named
token
in the POST request's form data
As a result, make sure your server-side app performs the following tasks:
- Handles POST requests for the initial page of the app
- Gets the token from the request's form data
- Validates the JWT token
Zendesk signs the JWT token with RSA using the SHA-256 hash algorithm ("RS256" in RFC7519). Your server-side app should use a JWT client library that supports this signature algorithm. Validating that the JWT algorithm used to encode the token is RS256 helps prevent downgrade attacks. A list of popular JWT libraries is available at jwt.io.
Your JWT library will need a public key to decode the token. You can get your app's public key with the Get App Public Key endpoint in the Zendesk REST API:
https://{subdomain}.zendesk.com/api/v2/apps/{app_id}/public_key.pem
where {subdomain}
is the subdomain of your Zendesk Support instance, and {app_id}
is the ID of your app. You can get your app id with the List All Apps endpoint:
https://{subdomain}.zendesk.com/api/v2/apps.json
The key is generated when an app is created in Zendesk Support and doesn't change if you update the app later.
Note: You can only get the app's public key if the app has already been created in a Zendesk Support account. So before you can modify your server-side app to validate the JWT token, you must upload the app package (consisting only of the manifest file and any in-product branding assets) to the Zendesk Support instance. (Uploading an app doesn't enable it in the user interface yet.)
The following example shows one way a server-side app can handle the Zendesk JWT token. The Ruby app uses the JWT gem, the Zendesk API client for Ruby, and the Sinatra web framework. The code is explained after the example.
require 'jwt'
require 'sinatra'
require 'zendesk_api'
client = ZendeskAPI::Client.new do |config|
## Change these values with your credentials
config.url = 'https://mysubdomain.zendesk.com/api/v2'
config.username = '[email protected]'
config.password = 'password'
end
app_id = 101 # Set this to your App ID
rsa_public_pem = client.connection.get("apps/#{app_id}/public_key.pem").body
puts "Validating against App ID #{app_id} with public key:"
puts rsa_public_pem
rsa_public = OpenSSL::PKey::RSA.new(rsa_public_pem)
set :protection, except: :frame_options
post '/' do
decoded_token = JWT.decode params[:token], rsa_public, true, algorithm: 'RS256'
jwt_claims = decoded_token.first
user_info = client.connection.get(jwt_claims["sub"]).body
user_name = user_info["user"]["name"]
account_url = jwt_claims["iss"]
"Welcome #{user_name} from #{account_url}!"
end
Note: If running this server-side app locally (example, http://localhost:4567), allow mixed content in your browser by clicking the shield icon on the right (Chrome) or the lock icon on the left (Firefox).
The app performs the following tasks:
Creates a REST API client with the Zendesk client for Ruby:
client = ZendeskAPI::Client.new do |config| ... end
Uses the client to make a Zendesk REST API request to get the app's public key:
rsa_public_pem = client.connection.get("apps/#{app_id}/public_key.pem").body
Uses a Sinatra url route to handle POST requests for the initial page of the app ('/'):
post '/' do ... end
Gets the token from the request and validates it:
post '/' do decoded_token = JWT.decode params[:token], rsa_public, true, algorithm: 'RS256' ...
For a Python example, see Secure the app in the "Building a server-side app" tutorial series in the Develop Help Center.
JWT claims
The JWT token contains a number of attributes (known as claims) that your server-side app can use to look up values associated with your Zendesk Support account.
Note: The JWT token does not grant access to any data in the Zendesk product instance apart from that provided in the JWT claims. An external method of authentication (such as username and password, API token, or OAuth) is required to fetch further information from the Zendesk product account, and is outside the scope of this document. For more information, see Security and Authentication in the Zendesk REST API documentation.
Claim Identifier | Name | Description | Example | Reference |
---|---|---|---|---|
exp | Expiration Time | The expiration time on or after which the JWT must not be accepted for processing | 1466728968 | RFC7519 Section 4.1.4 |
nbf | Not Before | The time before which the JWT must not be accepted for processing | 1466747798 | RFC7519 Section 4.1.5 |
iss | Issuer | The issuer of the token, in the form of the Zendesk Support account hostname | support.zendesk.com | RFC7519 Section 4.1.1 |
aud | Audience | The audience of which the token is valid for, in the form of a URI referencing the particular installation of the app which is being loaded | https://support.zendesk.com/api/v2/apps/installations/1000.json | RFC7519 Section 4.1.3 |
iat | Issued At | The time at which the JWT was issued, which can be used to determine the age of the JWT | 1466747858 | RFC7519 Section 4.1.6 |
sub | Subject | The subject of the JWT, in the form of a URI referencing the particular user that is loading the app | https://support.zendesk.com/api/v2/users/1000.json | RFC7519 Section 4.1.2 |
cnf | Confirmation | The identity of the proof-of-possession key, in the form of an object containing the URL to the app's public key, in JSON Web Key format | {"jku": "https://support.zendesk.com/api/v2/apps/100/public_key.json"} | RFC7800 Section 3.1 |
qsh | Query String Hash | A SHA256 hash of the canonical request string (method&uri-path&canonical-query-string ) |
bbe6b8ce792dccd999af6be72952d37c3bb07613d05c7576c5ff1d9eeed2ebdb | Atlassian Connect Documentation |
context | App Instance Context | An object containing the context in which the app is running, including the product and location properties |
{"context": {"product": "support", "location": "ticket_sidebar"}} | N/A |
Using local storage
App assets hosted by Zendesk have their own unique url. This means apps have their own local storage, which is not shared by other apps.
However, if users have different installations of the same app, the asset url will be the same for the different installations. As a result, it's good practice to scope local storage keys to each installation to prevent conflicts between different installations running on the same browser. Example:
var client = ZAFClient.init();
function setKey(key, val) {
return client.metadata().then(function(metadata) {
return localStorage.setItem(metadata.installationId + ":" + key, val);
});
}
function getKey(key) {
return client.metadata().then(function(metadata) {
return localStorage.getItem(metadata.installationId + ":" + key);
});
}
setKey("username", "agent_extraordinaire");
getKey("username").then(function(username) {
console.log(username); // agent_extraordinaire
});
Messaging between locations
The framework makes it possible for your app to interact with another instance of itself running in a different app location via the instances
API. For more information, see instances
.
Example
The example below demonstrates triggering an event from one instance and listening to it from another. For this example we named the event incoming_call
. In your own code, you can choose whatever name is appropriate.
The app in this example must run in the nav_bar
and top_bar
locations. The manifest would contain a snippet like this:
{
"location": {
"support": {
"nav_bar": "assets/nav_bar.html",
"top_bar": "assets/top_bar.html"
}
}
}
The top bar app runs the following code:
var client = ZAFClient.init();
client.on('incoming_call', function() {
client.invoke('popover');
});
The nav bar app runs the following code:
var client = ZAFClient.init();
var topBarClientPromise = client.get('instances').then(function(instancesData) {
var instances = instancesData.instances;
for (var instanceGuid in instances) {
if (instances[instanceGuid].location === 'top_bar') {
return client.instance(instanceGuid);
}
}
});
topBarClientPromise.then(function(topBarClient) {
// trigger an incoming_call event on the top bar
topBarClient.trigger('incoming_call');
});
Using modal dialogs
You can show an iframe in the modal location using the instances.create
API. Modals are currently only available in Zendesk Support.
Note: Apps using the signed urls feature must define the modal location in their manifest, specifying the url to be used.
Example
To open a modal that displays the current Wikipedia home page, use:
var client = ZAFClient.init();
client.invoke('instances.create', {
location: 'modal',
url: 'https://en.m.wikipedia.org/wiki/Main_Page',
size: { // optional
width: '450px',
height: '300px'
}
}).then(function(modalContext) {
// The modal is on the screen now!
var modalClient = client.instance(modalContext['instances.create'][0].instanceGuid);
modalClient.on('modal.close', function() {
// The modal has been closed.
});
});
Note: The maximum recommended width and height are 80vw and 80vh. Scrollbars are displayed if your content is larger than the width or height of the modal.
Testing and debugging
The Zendesk App Tools (ZAT) includes a local HTTP server that lets you run and test your apps locally.
Secure settings and app requirements don't work when an app is running locally. However, you can install the app as a private app and then use ZAT to make updates to the installed app. For secure settings, you can also run the app locally after installing the app remotely.
Testing an app locally
Use your command-line interface to navigate to the folder containing the app you want to test.
Run the following command in the app's folder to start the server:
$ zat server
In Zendesk Support, navigate to the page where you specified the app to appear and append "?zat=true" to the URL. Example:
https://subdomain.zendesk.com/agent/tickets/321321?zat=true
In Zendesk Chat, go to Visitors in the Dashboard, open a chat window by responding to or initiating a test chat, and insert ?zat=true in the chat window URL directly after /agent and before any # characters, then press Enter. Example:
https://subdomain.zendesk.com/chat/agent?zat=true#home#!1320285-fWgIEuaK2FUfZC
If the body of your app doesn't appear, check if your browser is blocking mixed content.
Because the app runs locally in an iframe in the Zendesk page, your browser may block it as mixed content. Firefox doesn't block this content but Safari does and has no option to disable blocking. Chrome blocks it by default but you can unblock it. Click the lock icon on the left side of the address bar, select Site Settings > Privacy and Security, then select the Allow option under Insecure content.
Test your app in the product interface and make any updates.
To stop the server, switch to your command-line interface and press Control+C.
Updating an installed app
After installing an app in Zendesk Support, you can continue making updates to the installed version.
If you uploaded the app manually, you must configure the updates first. See Configuring updates below. If you used the ZAT create command to upload the app, the configuration step is not necessary.
To update an installed app:
In your command-line tool, navigate to the app's local root folder. This is the folder that contains your manifest.json file.
Run
zat update
on the command line and enter your Zendesk Support subdomain, username, and password. The username is the email address you use to sign in.After the update is complete, refresh the apps in the user interface.
Configuring updates
If you manually uploaded the app, you must create a configuration file in your app's local root directory before you can make updates. This is a one-time requirement.
Note: If you used the ZAT create command to upload the app, the configuration file was created automatically. You can skip this section.
To create the configuration file:
Determine the app id by signing in to Zendesk Support as an admin and opening the following page in the same browser, replacing
your_subdomain
with your own:https://your_subdomain.zendesk.com/api/v2/apps/installations.json
Locate your app and note the
app_id
.Add a file named .zat to the root directory of your app.
Tip: On a Mac, open Terminal, navigate to the app's root directory, and run the following command:
$ touch .zat
In Windows, create the file in NotePad with File > Save As.
Add the following snippet to the file:
{ "subdomain": "your_subdomain", "username": "your_username", "app_id": 1234 }
Replace the property values with your own and save the file. The
username
value is the email address you use to sign in.
Testing an app with secure settings locally
If your app uses secure settings, you can keep testing it locally after installing it remotely.
Install the app as a private app in Zendesk Support.
Navigate to the app's root folder in your command-line tool and start the local ZAT server with an option named
app-id
:$ zat server --app-id=1234
Though named
app-id
, specify the app's installation id, not the app id. To find it, sign in to your Zendesk Support instance as an admin and open the following page in the same browser, replacingyour_subdomain
with your own:https://your_subdomain.zendesk.com/api/v2/apps/installations.json
Locate your installed app and note its
id
value, not itsapp_id
value.Test the app normally with the local server. See Testing an app locally.
Note: The domainWhitelist
of the most recently uploaded zip file will be used, not the value from the manifest file in zat server
.