On Enterprise plans, Zendesk help center themes can include client-side JavaScript that makes REST API calls and other HTTP requests from the browser. For example, your theme may include a custom button that makes requests to the Zendesk Ticketing API. As another example, your theme may include a custom page that accesses user-specific data from your back-end service.

Zendesk provides security tokens to verify client-side requests from the help center and ensure they're legitimate. The tokens used depend on whether you're sending the request to a Zendesk API or a third-party server:

Making Zendesk API requests with CSRF tokens

When signed in to Zendesk, users can authenticate client-side Zendesk API requests from the help center using their browser's Zendesk session cookie. No authentication credentials are required.

However, authenticated requests that create, update, or delete Zendesk data require a Cross-Site Request Forgery (CSRF) token for the user. This CSRF token helps Zendesk ensure the request originated from the user and the help center.

For example, if a user clicks a button that triggers a client-side Update Request call, the call must include a CSRF token. If the button instead triggers a Show Request call, a CSRF token isn't required.

Getting a CSRF token

You can retrieve the current user's CSRF token using a Show Self request.

fetch("/api/v2/users/me.json").then(response => {  console.log(response.json())})

The response includes the CSRF token in the authenticity_token property.

{  "user": {    "id": 1905826600027,    "url": "https://yoursubdomain.zendesk.com/api/v2/users/1905826600027.json",    "name": "John Doe",    ...    "authenticity_token": "W/oOCh3/...",    ...  }}

Passing the CSRF token in Zendesk API requests

When making an applicable Zendesk API request from the help center, include the token in the X-CSRF-Token HTTP header.

fetch("/api/v2/requests.json", {  method: "POST",  headers: {    "X-CSRF-Token": "W/oOCh3/...",    "Content-Type": "application/json"  },  body: JSON.stringify({    request: { subject: "Help!", comment: { body: "My printer is on fire!" } }  })}).then(response => {  console.log(response.json())})

Caching CSRF tokens

CSRF tokens provided by the Show Self endpoint last for the duration of the user session. To get a valid CSRF token for a new user session, send another Show Self request.

The Show Self endpoint is subject to API rate limits. Zendesk recommends caching CSRF tokens using the browser's local storage. Refresh the cache at set intervals, such as every two minutes.

Making third-party API requests with help center JWTs

On Enterprise plans, you can make help center requests to third-party servers. When doing so, you can optionally include a signed help center JSON Web Token (JWT).

The server that receives the request can then use a related JSON Web Key (JWK) to verify the JWT's signature. This verification ensures the request is legitimate and originated from the help center.

For example, you may set up middleware to receive requests from your help center. The middleware verifies each request's help center JWT to ensure the request is legitimate. If the request is legitimate, the middleware passes it on to your back-end service.

The help center JWT's payload includes the user's Zendesk email address, Zendesk user id, and external user id. The server receiving the JWT can use these identifiers to access and return related data for the user.

For example, your help center can send requests with help center JWTs to your online store's back-end API. Using identifiers from the JWT, you could fetch the current user's order history and display it on a custom page of the help center.

For an example of this setup, see Tutorial: Building a help center integration.

Getting the help center JWT

You can retrieve the current user's help center JWT using a New Token request.

fetch("/api/v2/help_center/integration/token.json").then(response => {  console.log(response.json())})

The response includes the JWT in the token property.

{  "token": "eyJ0eXAi..."}

JWT structure

Help center JWTs consist of three base64Url-encoded parts, separated by dots (.):

When decoded, the JWT's header is a JSON object describing the token and its format. The header contains the following fields.

FieldDescription
algAlgorithm used for the signature. Value is "RS256"
typToken type. Value is "jwt"
kidUnique id for the JWT's signing key

Payload

When decoded, the JWT's payload is a JSON object containing claims. For help center JWTs, these claims contain information about the request's origin and the token's expiration time.

The JWT's payload contains the following fields.

FieldDescription
issEntity that issued the JWT. Value is "Zendesk"
userIdZendesk user id for the user that made the request
emailPrimary email address for the user that made the request
externalIdExternal id for the user that made the request. If the user has no external id, the value is null
originHostname that originated the request. Value is "https://{YOUR_SUBDOMAIN}.zendesk.com"
expExpiration time for the JWT as Unix timestamp. Help center JWTs expire three minutes after creation

Signature

The signature is an RS256-signed string that contains the header, payload, and a secret signing key. A server receiving the JWT can use a related JWK to verify the signature.

Passing the JWT in third-party API requests

The request property used to pass a help center JWT varies based on the server receiving the request. For example, you can pass the JWT as a bearer token in the Authorization header.

const jwt = "YOUR_HC_JWT"
fetch("https://api.example.com/1", {  method: "DELETE",  headers: {    Authorization: `Bearer ${jwt}`  }}).then(response => {  console.log(response.json())})

Handling help center requests on your server

To receive requests from a help center, your server must enable Cross-Origin Resource Sharing (CORS). The server's Access-Control-Allow-Origin response header must allow requests from the help center's hostname, such as "https://example.zendesk.com". The steps for configuring CORS vary based on your server's software.

Getting a matching JWK

To verify help center JWTs, your server needs a matching JSON web key (JWK). You can use the List Public Keys endpoint to retrieve a list of valid JWKs for a Zendesk account.

curl -v https://{subdomain}.zendesk.com/api/v2/help_center/integration/keys.json

The response contains a keys array containing JWK objects. Use the kid in the JWT's header to find the matching JWK object.

{  "keys": [    {      "kty": "RSA",      "n": "yaiTIW...",      "e": "...",      "kid": "dee..."    },    ...  ]}

The JWK contains the components needed to construct the public signing key for the matching JWT's signature.

Verifying the JWT with the JWK

Use a client library to decode and verify the JWT using the JWK. A list of popular JWT libraries is available at jwt.io. This guide provides examples for the following languages:

Node.js

The following Node.js JavaScript snippet uses the jsonwebtoken and jwks-rsa libraries to:

  • Retrieve JWK objects from the List Public Keys endpoint
  • Find a matching JWK for the JWT using its kid
  • Get a public signing key for the JWT's signature
  • Verify the JWT's signature and expiration
  • Decode the JWT's payload
const jwt = require("jsonwebtoken")const jwksClient = require("jwks-rsa")
const ZENDESK_SUBDOMAIN = "YOUR_ZENDESK_SUBDOMAIN"// Be sure to create a single instance of the jwksClient and reuse it, to leverage the caching mechanismconst jwksClientInstance = jwksClient({  jwksUri: `https://${ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/help_center/integration/keys.json`});
// JWT to verify. In production, retrieve this from the incoming requestconst token = "eyJ0eXAi..."
const verifyToken = async token => {  const publicKey = await getPublicKeyFromJwks(    jwt.decode(token, { complete: true })  )  return jwt.verify(token, publicKey)}
const getPublicKeyFromJwks = async decodedToken => {  const kid = decodedToken.header.kid  const signingKey = await jwksClientInstance.getSigningKey(kid)  return signingKey.rsaPublicKey}
verifyToken(token).then(token => console.log(token))

Python

The following Python snippet uses the PyJWT and Requests libraries to:

  • Retrieve JWK objects from the List Public Keys endpoint
  • Find a matching JWK for the JWT using its kid
  • Get a public signing key for the JWT's signature
  • Verify the JWT's signature and expiration
  • Decode the JWT's payload
import jsonimport jwtimport requests
ZENDESK_SUBDOMAIN = "YOUR_ZENDESK_SUBDOMAIN"
# JWT to verify. In production, retrieve this from the incoming requestconst token = "eyJ0eXAi..."
def verify_token(token):    public_keys = get_public_keys_from_jwks()    decoded_header = jwt.get_unverified_header(token)    kid = decoded_header["kid"]    key = public_keys[kid]    return jwt.decode(token, key, algorithms=["RS256"])
def get_public_keys_from_jwks():    jwks_url = f"https://{ZENDESK_SUBDOMAIN}.zendesk.com/api/v2/help_center/integration/keys.json"    jwks_response = requests.get(jwks_url)    jwks_data = jwks_response.json()    public_keys = {}    for jwk in jwks_data["keys"]:        kid = jwk["kid"]        public_keys[kid] = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(jwk))    return public_keys
print(verify_token(token))

Caching JWTs and JWKs

JWTs provided by the New Token endpoint expire three minutes after creation. After expiration, you can refresh the token by sending another New Token request. JWKs provided by the List Public Keys endpoint may also be periodically rotated.

Zendesk recommends caching JWTs using the browser's local storage. Refresh the cache at set intervals, such as every two minutes. Zendesk also recommends caching JWKs on your server. Many client libraries for JWKs support caching with options for refreshing.